import javax.swing.*;
import pal.tree.*;
import java.io.*;
import java.net.*;
import java.util.*;
import pal.misc.*;
import pal.distance.*;


/**
*The purpose of the TreeUtils class is to act as a bin
*for methods for trees.  The methods are intended to be used
*by any "client" class.
*/
public class TreeUtils
	{

	/**
	*Given a string, return the string with all commas remvoed and spaces inserted in their place.
	*@param s a string to remove the commas from
	*@return s with spaces in place of commas - specifically, s.replace(","," ") is returned.
	*/
	public static String removeCommas(String s)
		{
		s=s.replace(","," ");
		return s;
		}


	/**
	*If s is a string of length <i>minGapLength</i>, this method returns a.indexOf(s).
	*@param a (used as above)
	*@param minGapLength, used as above
	*@return an int value computed as above.
	*/
	public static int getStartIndexOfLongGap(String a,int minGapLength)
		{
		String gap=new String("");
		for(int g=0;g<minGapLength;g++)
			{
			gap=gap+"-";
			}
		return a.indexOf(gap);
		}



	/**
	*Used to determine if an MSA needs trimming.  It returns an index of the of the first gap of
	*length minGapLength or (-1) if no such gap exists!
	*@param msa a String array representing a multiple sequence alignment
	*@param minGapLength when searching for gap areas to trim, this paramater is used as a min gap length
	*@return (-1) if no gaps of the min. length are found, or the index of the section where the gap exists
	*/
	public static int doesMSANeedTrimming(String[] msa,int minGapLength)
		{
		for(int s=0;s<msa.length;s++)
			{
			if(getStartIndexOfLongGap(msa[s],minGapLength)!=(-1))
				{
				return s;
				}
			}
		return (-1);
		}


	/**
	*Given a starting index, ending index, and an MSA, returns a trimmed MSA.
	*@param msa an MSA
	*@param startIndex the starting index of the place to trim at
	*@param endIndex the ending index of the place to trim
	*@return the msa given, but with the implied gap/section removed.
	*/
	public static String[] trimMSA(String[] msa,int startIndex,int endIndex)
		{		
		for(int s=0;s<msa.length;s++)
			{
			msa[s]=msa[s].substring(0,startIndex)+msa[s].substring(endIndex);
			}
		return msa;
		}


	/**
	*Given a pairwise alignment, indicated by a string with a single comma, returns the passed alignment
	*but with starting and ending gaps trimmed.  If there are no gaps at the beginning or end, then 
	*the argument is returned unchanged. (CSV stands for 'comma separated value)
	*@param c the pairwise alignment to trim (if needed)
	*@return the pairwise alignment trimmed if need be
	*/
	public static String removeStartingAndEndingGapGroupsFromAPairOfCSVSequences(String c)
		{
		
		String[] s=c.split(",");

		String a=new String(s[0]);
		String b=new String(s[1]);

		//remove gaps at the beginning
		while(
				a.startsWith("-") ||
				b.startsWith("-")
				)
			{
			a=a.substring(1);
			b=b.substring(1);
			}

		while(
			a.endsWith("-") ||
			b.endsWith("-")
				)
			{
			a=a.substring(0,a.length()-1);
			b=b.substring(0,b.length()-1);
			}

		return new String(a+","+b);
		}


	
	/**
	*This method replaces 'bad' newick string characters with okay ones.
	*Specifically, "(" are replaced by "[", ")" by "]" and "," by " ".
	*@param s, the string to make replacments with
	*@return the string with replacements done.
	*/
	public static String removeBadTreeChars(String s)
		{
		s=removeCommas(s);
		s=s.replace(")","]");
		s=s.replace("(","[");
		return s;
		}

	/**
	*Performs a Smith-Waterman local pairwise alignment and returns the alignment as an 
	*array of three strings.  The 0th item is the alignment score, the 1th item is the first
	*string (including any gaps), the 2nd item is the 2nd string (including any gaps)
	*@param i the first string to align
	*@param j the second string to alignt
	*@param m the match score used in the alignment in the dynamic programming algorithm
	*@param s the mismatch penalty
	*@param d the gap penalty
	*@return an array of strings giving the alignment score, and the pairwise alignments (of length 3).
	*/
	public static String[] SmithWaterman(String i,
						String j,
						double m,
						double s,
						double d)
		{


		/*PRELIMINARY TASKS, verify the data/parameters!*/
		if(i.length()<=0 || j.length()<=0)
			{
			throw new RuntimeException("Error - invalid string length in NW!");
			}

		/*make sure that the signs of the paramaters are known.*/
		m=Math.abs(m);
		s=Math.abs(s);
		d=Math.abs(d);

		/*Cast each of the strings to uppercase for easy comparisons.*/
		i=i.toUpperCase();
		j=j.toUpperCase();


		/*FIRST, create a matrix (call it w for Waterman) with 
		i.lenght()+1 rows and j.length()+1 columns*/
		double[][] w=new double[i.length()+1][];
		for(int initcol=0;initcol<i.length()+1;initcol++)
			{
			w[initcol]=new double[j.length()+1];
			}


		/*SECOND Fill the 0th row and 0th column initial value 
		of -0 and -0 as i and j range from 0 to the dimensions.
		*/
		for(int fillcollz=0;fillcollz<w[0].length;fillcollz++)
			{
			w[0][fillcollz]=0;
			}
		for(int fillrowz=0;fillrowz<w.length;fillrowz++)
			{
			w[fillrowz][0]=0;
			}



		/*THIRD, execute the algorithm according to the reccurence
		given in the Durbin book on pages 21-22 using the parameters passed
		to this method.  Also create and initialize an arrow matrix
		for construction of the actual alignment.
		*/


		//Build the arrow matrix and return array
		String[] returnArray=new String[3];
		for(int initra=0;initra<returnArray.length;initra++)
			{
			returnArray[initra]=new String("");
			}
		int[][] arrowArray=new int[w.length][];
		for(int ai=0;ai<arrowArray.length;ai++)
			{
			arrowArray[ai]=new int[w[ai].length];
			arrowArray[ai][0]=1;
			for(int ac=0;ac<arrowArray[0].length;ac++)
				{
				arrowArray[0][ac]=(-1);
				}
			}


		//EXECUTE THE SW ALGORITHM!!!!
		for(int row=1;row<w.length;row++)
			{
			for(int col=1;col<w[row].length;col++)
				{
				//System.out.println("Now considering row="+row+" and col="+col+".");

				double zero=0.0;
				double diag=0.0;
				double up=w[row-1][col]-d;
				double left=w[row][col-1]-d;
				if(i.substring(row-1,row).compareTo(j.substring(col-1,col))==0)
					{
					//A match!
					diag=w[row-1][col-1]+m;
					}
				else
					{
					//A mismatch!
					diag=w[row-1][col-1]-s;
					}
				
				double max=Math.max(zero,Math.max(diag,Math.max(up,left)));
				w[row][col]=max;
				arrowArray[row][col]=arrowVal(zero,up,diag,left);

				//pm(w);
				}
			}


		/*NOW, find the coordinates in the 
		matrix with the max value!*/
		int maxrow=0;
		int maxcol=0;
		for(int gr=0;gr<w.length;gr++)
			{
			for(int gc=0;gc<w[0].length;gc++)
				{
				if(w[gr][gc]>w[maxrow][maxcol])
					{
					maxrow=gr;
					maxcol=gc;
					}
				}
			}
		if(maxrow==0 && maxcol==0)
			{
			returnArray[0]=new String("0.0");
			returnArray[1]=new String("-");
			returnArray[2]=new String("-");
			return returnArray;
			}


		//construct the alignment...
		//returnArray[0]="The alignment score is : "+w[w.length-1][w[0].length-1]+".";
		returnArray[0]=new String(""+w[maxrow][maxcol]);
		int ai=maxrow;
		int aj=maxcol;


		//System.out.println("Showing the arrow array:");
		//ipm(arrowArray);

		while(
			w[ai][aj]!=0

			)
			{
			//System.out.println("The value of ai is "+ai+".");
			//System.out.println("The value of aj is "+aj+".\n");
			//System.out.println("The value of the matrix is "+arrowArray[ai][aj]+".");
			switch(arrowArray[ai][aj])
				{
				case 1:
					//System.out.println("moving up...");
					returnArray[1]=i.substring(ai-1,ai)+returnArray[1];
					returnArray[2]="-"+returnArray[2];
					ai--;
					break;
				case 0:
					//System.out.println("moving diag...");
					returnArray[2]=j.substring(aj-1,aj)+returnArray[2];
					returnArray[1]=i.substring(ai-1,ai)+returnArray[1];
					ai--;
					aj--;
					break;
				case (-1):
					//System.out.println("moving left...");
					returnArray[2]=j.substring(aj-1,aj)+returnArray[2];
					returnArray[1]="-"+returnArray[1];
					aj--;
					break;
				case 5:
				default:
					break;
				}
			}
		return returnArray;
		}



	private static int arrowVal(double z,double u,double d,double l)
		{
		if(0>d && 0>l && 0>u)
			{
			return 5;
			}
		else
			{
			return arrowVal(u,d,l);
			}

		}


	private static int arrowVal(double u,double d,double l)
		{
		if(u>d && u>l)
			{
			return 1;
			}
		if(l>u && l>d)
			{
			return -1;
			}
		else
			{
			return 0;
			}
		}




	/**
	*Given a string of comma-separated values, return an array of the value (without the commas)
	*@param csv a string of comma-separated values
	*@return csv.split(",");
	*/
	public static String[] makeArrayFromCSVStrings(String csv)
		{
		String[] t=csv.split(",");
		return t;
		}

	/**
	*Performs a Needleman-Wunsch global pairwise alignment and returns the alignment as an 
	*array of three strings.  The 0th item is the alignment score, the 1th item is the first
	*string (including any gaps), the 2nd item is the 2nd string (including any gaps)
	*@param i the first string to align
	*@param j the second string to alignt
	*@param m the match score used in the alignment in the dynamic programming algorithm
	*@param s the mismatch penalty
	*@param d the gap penalty
	*@return an array of strings giving the alignment score, and the pairwise alignments (of length 3).
	*/
	public static String[] NeedlemanWunsch(String i,
						String j,
						double m,
						double s,
						double d) 
		{

		/*PRELIMINARY TASKS, verify the data/parameters!*/
		if(i.length()<=0 || j.length()<=0)
			{
			throw new RuntimeException("Error - invalid string length in NW!");
			}

		/*make sure that the signs of the paramaters are known.*/
		m=Math.abs(m);
		s=Math.abs(s);
		d=Math.abs(d);

		/*Cast each of the strings to uppercase for easy comparisons.*/
		i=i.toUpperCase();
		j=j.toUpperCase();

		/*FIRST, create a matrix (call it w for Wunsch) with 
		i.lenght()+1 rows and j.length()+1 columns*/
		double[][] w=new double[i.length()+1][];
		for(int initcol=0;initcol<i.length()+1;initcol++)
			{
			w[initcol]=new double[j.length()+1];
			}

		/*SECOND Fill the 0th row and 0th column initial value 
		of -id and -jd as i and j range from 0 to the dimensions.
		*/
		for(int fillcollz=0;fillcollz<w[0].length;fillcollz++)
			{
			w[0][fillcollz]=(-1)*d*fillcollz;
			}
		for(int fillrowz=0;fillrowz<w.length;fillrowz++)
			{
			w[fillrowz][0]=(-1)*d*fillrowz;
			}

		/*THIRD, execute the algorithm according to the reccurence
		given in the Durbin book on page 20 using the parameters passed
		to this method.  Also create and initialize an arrow matrix
		for construction of the actual alignment.
		*/

		//Build the arrow matrix and return array
		String[] returnArray=new String[3];
		for(int initra=0;initra<returnArray.length;initra++)
			{
			returnArray[initra]=new String("");
			}
		int[][] arrowArray=new int[w.length][];
		for(int ai=0;ai<arrowArray.length;ai++)
			{
			arrowArray[ai]=new int[w[ai].length];
			arrowArray[ai][0]=1;
			for(int ac=0;ac<arrowArray[0].length;ac++)
				{
				arrowArray[0][ac]=(-1);
				}
			}

		//pm(w);


		//EXECUTE THE NW ALGORITHM!!!!
		for(int row=1;row<w.length;row++)
			{
			for(int col=1;col<w[row].length;col++)
				{
				//System.out.println("Now considering row="+row+" and col="+col+".");

				double diag=0.0;
				double up=w[row-1][col]-d;
				double left=w[row][col-1]-d;
				if(i.substring(row-1,row).compareTo(j.substring(col-1,col))==0)
					{
					//A match!
					diag=w[row-1][col-1]+m;
					}
				else
					{
					//A mismatch!
					diag=w[row-1][col-1]-s;
					}
				
				double max=Math.max(up,Math.max(diag,left));

				w[row][col]=max;
				arrowArray[row][col]=arrowVal(up,diag,left);

				//pm(w);
				}
			}

		//construct the alignment...
		//returnArray[0]="The alignment score is : "+w[w.length-1][w[0].length-1]+".";
		returnArray[0]=new String(""+w[w.length-1][w[0].length-1]);
		int ai=w.length-1;
		int aj=w[0].length-1;


		//System.out.println("Showing the arrow array:");
		//ipm(arrowArray);

		while(
			(ai>0 && aj>=0)
			||
			(aj>0 && ai>=0)

			)
			{
			//System.out.println("The value of ai is "+ai+".");
			//System.out.println("The value of aj is "+aj+".\n");
			//System.out.println("The value of the matrix is "+arrowArray[ai][aj]+".");
			switch(arrowArray[ai][aj])
				{
				case 1:
					//System.out.println("moving up...");
					returnArray[1]=i.substring(ai-1,ai)+returnArray[1];
					returnArray[2]="-"+returnArray[2];
					ai--;
					break;
				case 0:
					//System.out.println("moving diag...");
					returnArray[2]=j.substring(aj-1,aj)+returnArray[2];
					returnArray[1]=i.substring(ai-1,ai)+returnArray[1];
					ai--;
					aj--;
					break;
				case (-1):
					//System.out.println("moving left...");
					returnArray[2]=j.substring(aj-1,aj)+returnArray[2];
					returnArray[1]="-"+returnArray[1];
					aj--;
					break;
				default:
					break;
				}
			}
		return returnArray;
		}


	/**
	*Given a URL and some post data, post the data to the given URL.  This is essentially a 
	*wrapper for HTTP I/O.
	*@param postData the data to be posted
	*@param url the URL to post the data to.
	*@return the conent of the requested URL!
	*/
	public static String postDataToURLAndGetResponse(String postData,String url) throws Exception
		{

		//set up URL
		URL murl=new URL(url);
		URLConnection uc=murl.openConnection();

		//let URL read and write
		uc.setDoInput(true);
		uc.setDoOutput(true);
		uc.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
		DataOutputStream printout=new DataOutputStream(uc.getOutputStream());

		//set up post values
		String postContent=new String(postData);

		//send the postdata!!!!
		printout.writeBytes(postContent);
		printout.flush();
		printout.close();

		//get the results
		DataInputStream input=new DataInputStream(uc.getInputStream());
		String rr=new String("");
		String tots=new String("");
		while(null!=((rr=input.readLine())))
			{
			tots+=rr;
			}
		return tots;
		}




	/**
	*Given an array of strings which is of length 2, this methods sends a web
	*reques to crick to performa  bl2seq alignment.  The method returns the resulting alignment.
	*@param seqs an array of 2 sequences of length 2
	*@return the result of the remotely executed bl2seq alignment.
	*/
	public static String bl2seqAlignment(String[] seqs) throws Exception
		{
		//URL murl=new URL("http://crick.forensicbiodna.com/~esalina/rpc-php-utils.php");
		URL murl=new URL("http://crick.forensicbiodna.com/rpc-php-utils.php");
		URLConnection uc=murl.openConnection();
		uc.setDoInput(true);
		uc.setDoOutput(true);
		uc.setUseCaches(false);
		uc.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
		DataOutputStream printout=new DataOutputStream(uc.getOutputStream());
		String csvsequences=new String("");
		for(int seqn=0;seqn<seqs.length;seqn++)
			{
			csvsequences+=seqs[seqn];
			if(seqn<seqs.length-1)
				{
				csvsequences+=",";
				}
			}
		String content=new String("action=bl2seqaln&csvsequences="+URLEncoder.encode(csvsequences));
		printout.writeBytes(content);
		printout.flush();
		printout.close();
		DataInputStream input=new DataInputStream(uc.getInputStream());
		String rr=new String("");
		String tots=new String("");
		while(null!=((rr=input.readLine())))
			{
			tots+=rr;
			}


		return tots;
		}


	/**
	*Given an array of sequences (of any length!), this method sends a 
	*web request to crick to perform a MUSCLE alignment.
	*@param seqs an array of sequences
	*@return a string with commas separating the aligned sequences.
	*/
	public static String muscleAlignment(String[] seqs) throws Exception
		{
		URL murl=new URL("http://crick.forensicbiodna.com/~esalina/rpc-php-utils.php");
		URLConnection uc=murl.openConnection();
		uc.setDoInput(true);
		uc.setDoOutput(true);
		uc.setUseCaches(false);
		uc.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
		DataOutputStream printout=new DataOutputStream(uc.getOutputStream());
		String csvsequences=new String("");
		for(int seqn=0;seqn<seqs.length;seqn++)
			{
			csvsequences+=seqs[seqn];
			if(seqn<seqs.length-1)
				{
				csvsequences+=",";
				}
			}
		String content=new String("action=musclealign&csvsequences="+URLEncoder.encode(csvsequences));
		printout.writeBytes(content);
		printout.flush();
		printout.close();
		DataInputStream input=new DataInputStream(uc.getInputStream());
		String rr=new String("");
		String tots=new String("");
		while(null!=((rr=input.readLine())))
			{
			tots+=rr;
			}
		return tots;
		}




	/**
	*Given a string that is an array of aligned sequences, this method returns an array of
	*doubles that is a two-d array of the distances between the sequences.
	*@param alignments an array of strings that represents a multiple sequence alignment
	*@return a two-d array of doubles where the entries are pariwise distances of the sequences.  The distances are percent sequence similarity (what Nei & Kumar call 'p distance' in their book "Molecular Evolution And Phylogenetics")
	*/
	public static double[][] computeDistanceMatrix(String[] alignments)
		{

		double [][] dist=new double[alignments.length][];

		for(int a=0;a<alignments.length;a++)
			{
			dist[a]=new double[alignments.length];
			for(int b=0;b<alignments.length;b++)
				{
				if(a==b)
					{
					dist[a][b]=0.0;
					}
				else
					{
					dist[a][b]=computeDistance(alignments[a],alignments[b]);
					}
				}
			}
		return dist;
		}




	/**
	*Given a Sting that represents a SerialID, and a String representing a field from the specimen table in the database,
	*this method swends a web request to crick to do a database lookup.  The Serial is used as the primary key and the value in the field
	*specified by the field argument is returned.
	*@param serial the SerialID of the record whose field is to be returned
	*@param field the field whose value should be returned.
	*@return the value of the field of the record specificied by the serial ID
	*/
	public static String getDataGivenSerialFromServer(String serial,String field) throws Exception
		{
		//$serial=trim($_REQUEST['serial']);
		//$fieldname=trim($_REQUEST['field']);
		//URL u=new URL("http://crick.forensicbiodna.com/~esalina/rpc-php-utils.php?serial="+serial+"&fieldname="+field);
		//HttpURLConnection h=(HttpURLConnection)(u.openConnection());
		//h.setRequestMethod("GET");
        // Construct data
		URL yahoo = new URL("http://crick.forensicbiodna.com/~esalina/rpc-php-utils.php?action=getfieldvaluegivenserialid&serial="+URLEncoder.encode(serial,"UTF-8")+"&field="+URLEncoder.encode(field,"UTF-8"));
		BufferedReader in = new BufferedReader(
				new InputStreamReader(
				yahoo.openStream()));

		String inputLine=new String("");
		String currentLine=new String("");

		while ((currentLine = in.readLine()) != null)
			{
		    inputLine+=currentLine;
			}

		in.close();	
		return inputLine;
		}


	public static PhyloNode buildUnRootedTreeFromNJTree(NeighborJoiningTree NJTree)
		{

		//get the sequence
		String treeString=new String(NJTree.toString());

		//build a tree
		Object[] phyloTreeObjects=PhyloNode.makeTreeRec(treeString,true);
		PhyloNode entryNode=(PhyloNode)(phyloTreeObjects[0]);

		//return an entry node into the tree
		return entryNode;
		}


	/**
	*Given a newick string, return a phylogenetic tree and a PhyloNode that is an entrypoint into that tree
	*@param newickstring a newick string representation of a phylogenetic tree.
	*@return a PhyloNode that's an entrypoint into a phylogenentic tree (unrooted!) specified by the newick string.
	*/
	public static PhyloNode buildUnrootedTreeFromNewickString(String newickstring)
		{
		//get the sequence
		String treeString=new String(newickstring);

		//build a tree
		Object[] phyloTreeObjects=PhyloNode.makeTreeRec(treeString,true);
		PhyloNode entryNode=(PhyloNode)(phyloTreeObjects[0]);

		//return an entry node into the tree
		return entryNode;
		}



	/**
	*Returns ns=ns.replaceAll("DONE",""); where ns is the only argument.
	*@param ns a string
	*@return the value specified above
	*/
	public static String removeDoneFromDoneTerminatedNewickString(String ns)
		{
		//remove the 'DONE'
		ns=ns.replaceAll("DONE","");
		return ns;
		}


	/**
	*Given a newick string, make a phylogenetic tree (return a phylonode entrypoint to it) and root the tree using
	*the midpoint method.
	*@param ns a newick string
	*@return a PhyloNode that's an entrypoint into a phylogenetic tree where the node is the root of the tree where the
	*root was chosen using the midpoint method.
	*/
	public static PhyloNode makeRootedTreeMidpointMethodFromNewickString(String ns)
		{
		//Create a PhyloNode tree
		Object[] phyloTreeObjects=PhyloNode.makeTreeRec(ns,true);

		//Get the entry node
		PhyloNode entryNode=(PhyloNode)(phyloTreeObjects[0]);

		//return the root where the midpoint is.
		return rootUnrootedTreeWithMidPointOfLongestPath(entryNode);
		}



	/**
	*Given a newick string with "DONE;" at the end of it, remove "DONE" from the end of the string and return
	*the tree indicated with a PhyloNode which is the root and the entrypoint into the tree with the root
	*chosen using the midpoint method.
	*@param ns a DONE-terminated newick string
	*@return a PhyloNode that's an entrypoint and a root (chosen with the midpoint method) of a phylogenetic tree
	*/
	public static PhyloNode rootTreeFromDoneTerminatedNewickString(String ns)
		{
		
		//remove the 'DONE'
		ns=ns.replaceAll("DONE","");

		//Create a PhyloNode tree
		Object[] phyloTreeObjects=PhyloNode.makeTreeRec(ns,true);

		//Get the entry node
		PhyloNode entryNode=(PhyloNode)(phyloTreeObjects[0]);

		//return the root where the midpoint is.
		return rootUnrootedTreeWithMidPointOfLongestPath(entryNode);
		}



	/**
	*Given a PhyloNode that's an entrypoint into an unrooted phylogenetic tree, insert a root
	*using the midpoint method and return the inserted root.
	*@param entryNode an entrynode into an unrooted phylogenetic tree
	*@return an entrypoint node that's the root of the tree rooted with the midpoint algorithm.
	*/
	public static PhyloNode rootUnrootedTreeWithMidPointOfLongestPath(PhyloNode entryNode)
		{

		//get the longest path
		PhyloNode[] longPath=PhyloNode.getLongestPath(entryNode);

		//insert a root into the longest path and get a reference to it
		PhyloNode root=PhyloNode.insertRootGivenLongestPath(longPath);

		//return the newly inserted root
		return root;
		}


	/**
	*Given an array of Strings representing input data make a rooted tree (using the midpoint method)
	*and return the the PhyloNode that's the root of the tree!
	*@param inputData the data to build a tree from.
	*@return a PhyloNode that's the root of the tree chosen with the midpoint method
	*/
	public static PhyloNode makeARootedNJTreeFromInputData(String[] inputData)
		{		
		//Build the NJ Tree
		NeighborJoiningTree myTree=buildNJTreeFromInputData(inputData);

		System.out.println("The tree created is "+myTree.toString()+".");

		//Create a PhyloNode tree
		PhyloNode treeEntryNode=buildUnRootedTreeFromNJTree(myTree);

		//Root the PhyloNode Tree
		PhyloNode root=rootUnrootedTreeWithMidPointOfLongestPath(treeEntryNode);
		//System.out.println("Now tring to root the tree at the oldest node :");
		//PhyloNode root=treeEntryNode.insertRootNearNodeWithSerial(treeEntryNode,"EU573940");

		//Return the root of the tree
		return root;
		}



	/**
	*Given a distance matrix and a string of sequence descriptions, build a tree
	*(using PAL) and return a newick string of the generated tree.
	*@param dm a distance matrix
	*@param desc an array of sequence descriptions
	*/
	public static String buildNJTreeFromDescriptionsAndDistanceMatrix(double[][] dm,String[] desc)
		{
		int numSeqs=desc.length;
		SimpleIdGroup myIdGroup=new SimpleIdGroup(numSeqs);
		Identifier[] seqIds=new Identifier[numSeqs];

		//create the identifiers and the identifier group
		for(int initidgrp=0;initidgrp<numSeqs;initidgrp++)
			{
			seqIds[initidgrp]=new Identifier(desc[initidgrp]);
			myIdGroup.setIdentifier(initidgrp,seqIds[initidgrp]);
			}


		//read the distances into 'half' a matrix
		double[][] halfDist=new double[dm.length][];
		for(int hi=0;hi<dm.length;hi++)
			{
			halfDist[hi]=new double[dm.length];
			for(int hic=0;hic<hi;hic++)
				{
				halfDist[hi][hic]=dm[hi][hic];
				halfDist[hic][hi]=dm[hi][hic];
				}
			}
		
		DistanceMatrix PalDm=new DistanceMatrix(halfDist,myIdGroup);

		
		NeighborJoiningTree myTree=new NeighborJoiningTree(PalDm);

		return myTree.toString();
		}








	/**
	*Given an array of strings representing input data, using PAL, build a neighbor-joining tree.
	*@param inputData the input data for the tree
	*@return a NeighborJoining tree created by PAL.
	*/
	public static NeighborJoiningTree buildNJTreeFromInputData(String[] inputData)
		{
		//BUILD AN NJ TREE FROM THE INPUT LINES!
		int numSeqs=Integer.parseInt(inputData[0]);
		SimpleIdGroup myIdGroup=new SimpleIdGroup(numSeqs);
		Identifier[] seqIds=new Identifier[numSeqs];

		//create the identifiers and the identifier group
		for(int initid=0;initid<numSeqs;initid++)
			{
			seqIds[initid]=new Identifier(inputData[initid+1]);
			myIdGroup.setIdentifier(initid,seqIds[initid]);
			}

		//build a distance matrix
		double[][] distanceMatrix=new double[numSeqs][];
		for(int initrows=0;initrows<numSeqs;initrows++)
			{
			distanceMatrix[initrows]=new double[numSeqs];
			}

		//read the data into "half" of the matrix
		int fileLine=numSeqs+1;
		for(int rinit=1;rinit<numSeqs;rinit++)
			{
			for(int cinit=0;cinit<rinit;cinit++)
				{
				distanceMatrix[rinit][cinit]=Double.parseDouble(inputData[fileLine].split(",")[cinit]);
				}
			fileLine++;
			}

		//make the matrix symmetrical
		for(int sr=0;sr<numSeqs;sr++)
			{
			for(int cr=0;cr<numSeqs;cr++)
				{
				distanceMatrix[sr][cr]=Math.max(distanceMatrix[sr][cr],distanceMatrix[cr][sr]);
				}
			}
	
		//create the PAL distance matrix	
		DistanceMatrix palDM=new DistanceMatrix(distanceMatrix,myIdGroup);

		//create the PAL NeighborJoiningTree
		NeighborJoiningTree myTree=new NeighborJoiningTree(palDM);

		return myTree;
		}


	/**
	*Given an array of strings (of length <i>l</i>) (likely read from a FASTA file), return an array of array of strings
	*where the first array is an array of descriptions and the second array is an array of sequences
	*@param fl an array of fasta lines
	*@return an array of array of strings (consisting of exactly two arrays), where the first array is an array of the fasta descriptions (of length l/2) and the
	*second array is an array of the fasta sequences of length l/2 as well.
	*/
	public static String[][] separateDescriptionsAndAlignmentsFromFastaLines(String[] fl)
		{
		String[] desc=null;
		String[] alignments=null;

		for(int ln=0;ln<fl.length;ln++)
			{
			if((ln%2)==0)
				{
				desc=addLine(fl[ln],desc);
				}
			else
				{
				alignments=addLine(fl[ln],alignments);
				}
			}

		String[][] res=new String[2][];
		res[0]=desc;
		res[1]=alignments;

		return res;
		}


	/**
	*Given an array of strings, append an additional string to the array
	*@param existing the array of strings to append to (of length l)
	*@param newLine the string to append
	*@return an array of string of length l+1 with the existign array and the string argument appended to it!
	*/
	public static String[] addLine(String newLine,String[] existing)
		{
		String[] toBeReturned=null;

		if(existing==null)
			{
			toBeReturned=new String[1];
			toBeReturned[0]=new String(newLine);
			return toBeReturned;
			}
		else if(existing.length==0)
			{
			toBeReturned=new String[1];
			toBeReturned[0]=new String(newLine);
			return toBeReturned;
			}
		else
			{
			toBeReturned=new String[existing.length+1];

			for(int index=0;index<existing.length;index++)
				{
				toBeReturned[index]=new String(existing[index]);
				}
			toBeReturned[existing.length]=new String(newLine);
			return toBeReturned;
			}
		}


	/**
	*Given a String[] array, return a concatenation String of all the lines!
	*/
	public static String concat(String[] l)
		{
		if(l==null)
			{
			return null;
			}
		String lines=new String("");
		for(int i=0;i<l.length;i++)
			{
			lines=lines+l[i];
			}
		return lines;
		}



	/**
	*Given a string pointing to a filename, return a single string
	*concatenation of the file lines
	*/
	public static String getFileLinesInOneLine(String filePath)
		{
		return concat(readStringFromFile(filePath));
		}



	/**
	*Given a String representing a file with a file name, 
	*read the file into an array of strings and return the array.
	*@return an array of strings read from the file.
	*/
	public static String[] readStringFromFile(String treeFile)
		{
		try
			{
			//open the file and read the stuff....

			BufferedReader mbr=null;
			File myFile=new File(treeFile);
			FileReader mfr=new FileReader(myFile);
			mbr=new BufferedReader(mfr);
			
			String[] l=null;
			String i=null;

			while(mbr.ready())
				{
				i=mbr.readLine();
				while(i.indexOf(" ")!=(-1) && i.indexOf("\t")!=(-1) && i.indexOf("\n")!=(-1))
					{
					i=i.replaceAll(" ","");
					i=i.replaceAll("\t","");
					i=i.replaceAll("\n","");
					}
				l=addLine(i,l);
				}
			mbr.close();
			return l;

			}
		catch(Exception e)
			{
			e.printStackTrace();
			System.exit(0);
			}
		System.exit(0);
		return null;
		}


	private static PhyloNode getRootAt(PhyloNode near)
		{
		if(near.neighbors.length==0)
			{
			return near;
			}
		else if(near.neighbors.length==1)
			{
			//get root here
			PhyloNode connection=near.neighbors[0];
			double dist=near.getDisanceToImmediateNeighbor(connection);
			connection.removeNeighbor(near);
			near.removeNeighbor(connection);
			PhyloNode newRoot=new PhyloNode(new String("newRoot"));
			newRoot.addNeighbor(connection,dist/2.0);
			newRoot.addNeighbor(near,dist/2.0);
			return newRoot;
			}
		else
			{
			throw new RuntimeException("Error - can't get root from internal phylonode with multiple neighbors!!!!");
			}

		}

	/**
	*Given a root cluster (of a tree), the name of a node in the tree, and a newick string of the tree
	*This method creates a phylogenetic tree using the information and roots the tree at the node 
	*specified by the string n.
	*@param rootCluster the rootCluster of the tree to find the cluser which will be the new root.
	*@param n the name of the cluster to root the tree at.
	*@param newickString a newick string representing the tree to find the new root in
	*/
	public static PhyloCluster getNewRootGivenName(PhyloCluster rootCluster,String n,String newickString)
		{
		/*
		public static int showConfirmDialog(Component parentComponent,
                                  Object message)
		*/
		//make a tree
		PhyloNode treeEntryNode=buildUnrootedTreeFromNewickString(newickString);
		PhyloNode nodeToRootBy=treeEntryNode.findNodeByName(n);
		if(nodeToRootBy==null)
			{
			JOptionPane.showMessageDialog(null,"You First Must RESET all the names in the naming window - sorry.");
			return null;
			}
		else
			{
			PhyloNode newRoot=getRootAt(nodeToRootBy);
			PhyloCluster newRootCluster=new PhyloCluster(newRoot,null,null,true);
			return newRootCluster;
			}
		}



	/**
	*Given two strings, expected to be sequences, compute the distance between the two strings using 
	*what Nei & Kumar call 'p distance' in their book "Molecular Evolution And Phylogenetics"
	*@param s1 the first sequence
	*@param s2 the second sequence
	*@return the p distance between the two strings
	*/
	public static double computeDistance(String s1,String s2)
		{
		if(s1.length()!=s2.length())
			{
			throw new RuntimeException("Error - trying to compute distance between strings of unequal length!!!!");
			}
		else
			{
			String[] s1chars=new String[s1.length()];
			String[] s2chars=new String[s2.length()];
			double totalChars=0.0;
			double numSame=0.0;
			for(int x=0;x<s1chars.length;x++)
				{
				s1chars[x]=new String(s1.substring(x,x+1));
				s2chars[x]=new String(s2.substring(x,x+1));
				totalChars+=1.0;
				if(s1chars[x].compareTo(s2chars[x])==0)
					{
					numSame+=1.0;
					}				
				}
			double percentSame=numSame/totalChars;
			double dist=1.0-percentSame;
			return dist;
			}
		}


	/**
	*Given a String that's a comma-separated list of SerialIDs, send the SerialIDs
	*to the crick server so that it can chose the best root by date.
	*@param csvSerialList a string of comma-separated SerialIDs
	*@return a String that's the SerialID of the oldest sequence among those indicated by the SerialIDs
	*/
	public static String sendCSVSerialsToGetBestRoot(String csvSerialList)
		{
		String postData=("serials="+csvSerialList);
		String url=new String("http://www.forensicbiodna.com/rootUtils.php");
		String result=new String("midpoint");

		try
			{
			result=postDataToURLAndGetResponse(postData,url);
			}
		catch(Exception e)
			{
			System.out.println("EXCEPTION CAUGHT IN TRYING TO GET OLDEST NODE FROM SERVER!!!!: \n");
			e.printStackTrace();
			}
		return result;
		}



	/**
	*Given a newick String, build a tree, root it using the "oldest sequence" method (by date!)
	*and root the tree there and return that node.
	*@param newickString a string indicating a phylogenetic tree
	*@return a PhyloNode that's an entrypoint into a tree such that the phylonode's sequence is the oldest
	*/
	public static PhyloNode chooseRootAndRootTree(String newickString)
		{
		//make a tree
		PhyloNode treeEntryNode=buildUnrootedTreeFromNewickString(newickString);

		//get the serials in the tree
		String[] serialList=PhyloNode.getSerialsOfNodes(treeEntryNode);

		//put the serials in a single csv string
		String csvSerialList=new String("");
		String bestRoot=null;
		if(serialList!=null && serialList.length!=0)
			{
			for(int s=0;s<serialList.length;s++)
				{
				csvSerialList=csvSerialList+serialList[s];
				if(s!=serialList.length-1)
					{
					csvSerialList=csvSerialList+new String(",");
					}
				}
			//ask the server what the oldest serial is:
			System.out.println("Now asking the server who the oldes serial is given the list : "+Arrays.toString(serialList)+".\n");
			bestRoot=sendCSVSerialsToGetBestRoot(csvSerialList);
			System.out.println("According to the function call, the oldest sequence is "+bestRoot+".");
			}
		else
			{
			bestRoot=new String("midpoint");
			}

		if(bestRoot.compareTo("midpoint")==0)
			{
			return rootUnrootedTreeWithMidPointOfLongestPath(treeEntryNode);
			}
		else
			{
			PhyloNode newRoot=PhyloNode.insertRootNearNodeWithSerial(treeEntryNode,bestRoot);
			return newRoot;
			}
		}


	/**
	*Given a string that's a comma-separated list of SerialIDs, return the SerialID that's the oldest!
	*@param csvSerialList a string of comma-separated SerialIDs
	*@return a String that's a SerialID whose sequence is the oldest among the list.
	*/
	public static String getSerialOfOldestSequenceFromSerials(String csvSerialList)
		{
		int minYear=9999;
		int year;
		String yearString=null;
		String minSerial=null;
		String[] serialList=csvSerialList.split(",");
		for(int sn=0;sn<serialList.length;sn++)
			{
			try
				{
				yearString=getDataGivenSerialFromServer(serialList[sn],"collection_year");
				year=Integer.parseInt(yearString);
				if(year!=0)
					{
					if(minYear>year)
						{
						minYear=year;
						minSerial=new String(serialList[sn]);
						}	
					}
				}
			catch(Exception e)
				{
				e.printStackTrace();
				return serialList[0];
				}
			}
		if(minYear==9999 || minSerial==null)
			{
			return serialList[0];
			}
		else
			{
			return minSerial;
			}
		}





	}
