/*
#************************************************************
#************************************************************
#
# Date:   3/25/08                Coded by:  Eddie Salinas
# Version: 1.0				     Module name:  PhyloCanvas.java
# Filename: PhyloCanvas.java   Source file:  PhyloCanvas.java
# Program description:
The purpose of this file is to take an unrooted tree (graph of interconnected
PhyloNode objects) and build from it a binary rooted tree.  The root of the
tree is prechosen and the constructor of this class is given the phylonode
root and traverses the graph (recursively) to build a binary tree.
#************************************************************
# Libraries and software support:
java version "1.5.0_11"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_11-b03)
BEA JRockit(R) (build R27.3.1-1_CR336511-87524-1.5.0_11-20070817-1356-linux-x86_64, compiled mode)
#************************************************************
# Development environment:
java version "1.5.0_11"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_11-b03)
BEA JRockit(R) (build R27.3.1-1_CR336511-87524-1.5.0_11-20070817-1356-linux-x86_64, compiled mode)

#************************************************************
# System requirements:
java version "1.5.0_11"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_11-b03)
BEA JRockit(R) (build R27.3.1-1_CR336511-87524-1.5.0_11-20070817-1356-linux-x86_64, compiled mode)
#************************************************************
# Start date:
# Update history:
#            DATE            MODIFICATION
#
#************************************************************
# Test history:
#  TEST PROTOCOL           DATE       TEST RESULTS
#
#************************************************************
# Programmer comments:
see purpose above
#************************************************************
*/

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

/**
*The PhyloCluster class is used to reprsent nodes of a phylogenetic tree, not in a
*general/abstract graph format, but rather in a binary tree format with a distinct and
*definite root.  To this purpose, each cluster has a name (unused for internal nodes)
*and a left-child and a right child.  For 'leaf' clusters, the children are null, for
*internal cluster, the children are non-null.
*/
public class PhyloCluster
	{

	/**
	*The name of this cluster.
	*/
	public String name=new String("");
	private String originalName=null;
	Color clusterColor=Color.BLACK;
	double clusterDistance=0.0;
	PhyloCluster leftChild=null;
	PhyloCluster rightChild=null;
	PhyloCluster parent=null;
	int nameLengthInPixels;

	/**
	*Get the left child of this cluster.
	*@return the PhyloCluster that's the left child of this cluster.
	*/
	public PhyloCluster getLeftChild()
		{
		return leftChild;
		}

	/**
	*Get the right child of this cluster.
	*@return the PhyloCluster that's the right child of this cluster.
	*/
	public PhyloCluster getRightChild()
		{
		return rightChild;
		}




	public PhyloCluster getChildByName(String targetName)
		{
		
		if(name!=null && name.compareToIgnoreCase(targetName)==0)
			{
			return this;
			}
		else
			{
			if(leftChild==null && rightChild==null)
				{
				return null;
				}
			else
				{
				PhyloCluster target=null;
				target=leftChild.getChildByName(targetName);
				if(target!=null)
					{
					return target;
					}
				return rightChild.getChildByName(targetName);
				}
			}		
		}







/**
 * Using this cluster's name, this method computes the names's length
 * in pixel and stores the value in this objects member variable.
 * @param g the graphics context in which to compute the cluster's name's length
 */
	private void storeNameLength(Graphics2D g)
		{
		FontMetrics fm=g.getFontMetrics();
		String siq=new String(this.name);
		int sl=SwingUtilities.computeStringWidth(fm,siq);
		nameLengthInPixels=sl;
		}

	/**
	*Keeps track of the current x translation
	*/
	public static int currenttransx=0;
	/**
	*Keeps track of the current y translation
	*/
	public static int currenttransy=0;

	/**
	*Keep track of the maximum x translation
	*/
	public static int maxx=0;
	/**
	*Keep track of the minimum x translation
	*/
	public static int minx=0;
	/**
	*Keep track of the maximum y translation
	*/
	public static int maxy=0;
	/**
	*Keep track of the minimum y translation
	*/
	public static int miny=0;

	/**
	*Prints the values of maxx,maxy,minx, and miny to the standard out.
	*/
	public static void tellAboutMAM()
		{
		System.out.println("Max x "+maxx+".");
		System.out.println("Max y "+maxy+".");
		System.out.println("Min x "+minx+".");
		System.out.println("Min y "+miny+".");
		}


	/**
	*Computes abs(max-minx)
	*@return abs(maxx-minx)
	*/
	public static double getMaxXDiff()
		{
		double dmaxx=(double)(maxx);
		double dminx=(double)(minx);
		double diff=Math.abs(maxx-minx);
		return (int)(diff);
		}

	/**
	*Sets the translation variables to zero as well ax (max/min)(x/y) to zero.
	*/
	public static void rebootMaxAndMin()
		{
		currenttransx=0;
		currenttransy=0;
		maxx=0;
		minx=0;
		maxy=0;
		miny=0;
		}


	/**
	*Called recursively, this method traverses the tree so that the nodes with larger
	*children are on the right.
	*/
	public void largeOnRight()
		{

		if(leftChild==null || rightChild==null)
			{
			return;
			}
		else
			{
			if(rightChild.getNumProgeny()<leftChild.getNumProgeny())
				{
				swapChildren();
				}
			leftChild.largeOnRight();
			rightChild.largeOnRight();
			}
		}


	/**
	*Causes this node and its subtree to 'spread'.
	*If this cluster's left child is non-null, then it gets its largeOnLeft
	*method called and then it's spread method is called.  If this cluster's
	*right child is non-null, then its largeOnRight method is called 
	*and then it's spread method is called.
	*/
	public void spread()
		{
		if(leftChild!=null)
			{
			leftChild.largeOnLeft();
			leftChild.spread();
			}
		if(rightChild!=null)
			{
			rightChild.largeOnRight();
			rightChild.spread();
			}
		}


	/**
	*Called recursively, this method traverses the tree so that the nodes with larger
	*children are on the left.
	*/
	public void largeOnLeft()
		{		
		if(leftChild==null || rightChild==null)
			{
			return;
			}
		else
			{
			if(leftChild.getNumProgeny()<rightChild.getNumProgeny())
				{
				swapChildren();
				}
			leftChild.largeOnLeft();
			rightChild.largeOnLeft();
			}
		}


	/**
	*Swaps this cluster's children so that the left childen become the right child and vice-versa.
	*/
	public void swapChildren()
		{
		PhyloCluster tempCluster;
		tempCluster=leftChild;
		leftChild=rightChild;
		rightChild=tempCluster;
		}

	/**
	*A setter method for this cluster's color.
	*@param newColor the new color for this node!
	*/
	public void setColor(Color newColor)
		{
		this.clusterColor=newColor;
		}

	/**
	*This method sets the color for this node and for its substree recursively.
	*@param newColor the new color for this node and its subtree.
	*/
	public void setColorRec(Color newColor)
		{		
		this.clusterColor=newColor;		
		if(leftChild!=null)
			{
			leftChild.setColorRec(newColor);
			}
		if(rightChild!=null)
			{
			rightChild.setColorRec(newColor);
			}
		}

	/**
	*Get the distance to this cluster's left Child
	*@return the distance to this cluster's left child
	*/
	public double getLeftDistance()
		{
		return leftChild.getClusterDistance();
		}


	/**
	*Get the distance to this cluster's right Child
	*@return the distance to this cluster's right child
	*/
	public double getRightDistance()
		{
		return rightChild.getClusterDistance();
		}


	/**
	*Get the distance of this cluster.
	*/
	public double getClusterDistance()
		{
		return clusterDistance;
		}


	/**
	*Get an array of all the leaves in the subtree specified by this cluster.
	*@return an array of all the leaves in the subtree specified by this cluster.
	*/
	public PhyloCluster[] getLeafChildren()
		{
		//	public boolean isValidLeaf()
		//	public boolean isValidParent()
		PhyloCluster[] refs=null;

		if(isValidLeaf())
			{	
			refs=new PhyloCluster[1];
			refs[0]=this;
			return refs;
			}
		else if(isValidParent())
			{
			PhyloCluster[] leftrefs=leftChild.getLeafChildren();
			PhyloCluster[] rightrefs=rightChild.getLeafChildren();
			refs=mergeLists(leftrefs,rightrefs);
			return refs;
			}
		else
			{
			throw new RuntimeException("Error - invalid cluster!!!!!!");
			}
		}


	/**
	*Get an array of the clusters in this cluster's subtree (including this cluster)
	*@return an array of the clusters in this cluster's subtree (including this cluster)
	*/
	public PhyloCluster[] getSelfAndAllProgeny()
		{		
		if(isValidLeaf())
			{
			return getLeafChildren();
			}
		else if(isValidParent())
			{			
			PhyloCluster[] selfref=new PhyloCluster[1];
			selfref[0]=this;
			PhyloCluster[] leftrefs=leftChild.getSelfAndAllProgeny();
			PhyloCluster[] rightrefs=rightChild.getSelfAndAllProgeny();
			PhyloCluster[] childrefs=mergeLists(leftrefs,rightrefs);
			PhyloCluster[] allrefs=mergeLists(selfref,childrefs);
			return allrefs;
			}
		else
			{
			throw new RuntimeException("Error - invalid cluster!!!!!!");
			}

		}
	
	/**
	*Given an array e of PhyloClusters and an additional PhyloCluster a, this method
	*appends a to the end of the array and returns the array.
	*@param e an array of PhyloClusters
	*@param a a single PhyloCluster
	*@return an array of Clusters composed of e with a appended
	*/
	public static PhyloCluster[] addToList(PhyloCluster[] e,PhyloCluster a)
		{
		PhyloCluster[] tbr=null;
		if(e==null)
			{
			tbr=new PhyloCluster[1];
			tbr[0]=a;
			return tbr;
			}
		else
			{
			
			tbr=new PhyloCluster[e.length+1];
			for(int c=0;c<e.length;c++)
				{
				tbr[c]=e[c];
				}
			tbr[tbr.length-1]=a;
			return tbr;

			}
		}


	/**
	*From the subtree rooted at this cluster, get all the internal, nonleaf nodes.
	*@return the set of nonleaf nodes from the subtree rooted at this cluster.
	*/
	public PhyloCluster[] getValidInternalNodes()
		{
		PhyloCluster[] internals=null;
		PhyloCluster[] all=getSelfAndAllProgeny();
		for(int i=0;i<all.length;i++)
			{
			if(all[i].isValidParent() && !all[i].isValidLeaf())
				{
				internals=addToList(internals,all[i]);
				}
			}
		return internals;
		}



	/**
	*Given two arrays of clusters, concatenate them together with the first argument 1st in the concatenation.
	*@param a a non-null array of clusters
	*@param b a non-null array of clusters
	*@return an array of clusters x of length a.length+b.length with elements a[0],a[1]...a[a.length-1],b[0],b[1],...b[b.length-1]
	*/
	private static PhyloCluster[] mergeLists(PhyloCluster[] a,PhyloCluster[] b)
		{

		PhyloCluster[] tot=new PhyloCluster[a.length+b.length];
		int ni=0;

		for(int ai=0;ai<a.length;ai++)
			{
			tot[ai]=a[ai];
			ni++;
			}

		for(int bi=0;bi<b.length;bi++)
			{
			tot[ni+bi]=b[bi];
			}

		return tot;
		}



	/**
	*From the subtree rooted at this cluster, return a cluster at random.
	*@param allowChooseRoot allow the randomly chosen cluster to be this node
	*@param minNumProg the minimum number of progeny desired from the subtree rooted at the returned random node
	*@param stop a value in the range [0,1] giving an idea of how far down the subtree rooted at this cluster to find a node
	*@return a random node in the subtree rooted at this cluster
	*/
	public PhyloCluster chooseRandomCluster(boolean allowChooseRoot,int minNumProg,double stop)
		{
		double randomChoice=Math.random();
		double randomStop=Math.random();
		if(randomStop<=stop && allowChooseRoot)
			{
			return this;
			}
		if(getNumProgeny()<=minNumProg)
			{
			return this;
			}
		else
			{
			if(randomChoice<=0.5)
				{
				return leftChild.chooseRandomCluster(true,minNumProg,stop);
				}
			else
				{
				return rightChild.chooseRandomCluster(true,minNumProg,stop);
				}
			}
		}



	/**
	*Creates a PhyloCluster from a PhyloNode.  This method calls itself recursively.
	*@param root the PhyloNode which represents the root of the tree
	*@param rooter used in recursive calls so that a path of recursion isn't traced backwards!
	*@param PhyloCluster iParent used as a reference to a parent cluster
	*@param first when called nonrecursively, should definitely be set to <i>FALSE</i>!
	*/
	public PhyloCluster(PhyloNode root,PhyloNode rooter,PhyloCluster iParent,boolean first)
		{
		//First, handle base cases with with nodes having ZERO neighbors
		if(root.neighbors==null)
			{
			parent=iParent;
			name=new String(root.getName());
			originalName=new String(name);
			clusterDistance=1.0;
			return;
			}
		if(root.neighbors.length==0)
			{
			parent=iParent;
			name=new String(root.getName());
			originalName=new String(name);
			clusterDistance=1.0;
			return;
			}

		//try to avoid cases where there are 4 or more neighbors!
		//root.makeBinaryWhereNeeded(null);

		//all nodes should have 0, 1, or 2 neighbors!
		if(root.neighbors.length!=1 && root.neighbors.length!=3 && root.neighbors.length!=2)
			{
			throw new RuntimeException("Error - trying to root a cluster at a node with "+root.neighbors.length+" number of children!");
			}


		//base case: leaves
		if(root.neighbors.length==1 && root.neighbors[0]==rooter)
			{
			System.out.println("Handling a base case with root "+root.getName()+".");
			name=new String(root.getName());
			originalName=new String(root.getName());
			parent=iParent;
			clusterDistance=root.getDisanceToImmediateNeighbor(rooter);
			return;
			}
		if(root.neighbors.length==1 && root.neighbors[0]!=rooter && rooter!=null)
			{
			throw new RuntimeException("Error - a root doesn't have a rooter as its neighbor!");
			}

		name=new String("");
		originalName=new String("");

		if(root.neighbors.length==2)
			{
			//this should be the initial call
			if(rooter==root.neighbors[0] || rooter==root.neighbors[1])
				{
				name=null;
				originalName=null;
				throw new RuntimeException("Bad initial call!!! - first="+first+"");
				}
			parent=iParent;
			leftChild=new PhyloCluster(root.neighbors[0],root,this,false);
			rightChild=new PhyloCluster(root.neighbors[1],root,this,false);
			clusterDistance=(leftChild.clusterDistance+rightChild.clusterDistance)/2.0;
			return;
			}
		if(root.neighbors.length==3)
			{
			parent=iParent;
			clusterDistance=rooter.getDisanceToImmediateNeighbor(root);
			if(rooter==root.neighbors[0])
				{
				leftChild=new PhyloCluster(root.neighbors[1],root,this,false);
				rightChild=new PhyloCluster(root.neighbors[2],root,this,false);
				}
			if(rooter==root.neighbors[1])
				{
				leftChild=new PhyloCluster(root.neighbors[0],root,this,false);
				rightChild=new PhyloCluster(root.neighbors[2],root,this,false);
				}
			if(rooter==root.neighbors[2])
				{
				leftChild=new PhyloCluster(root.neighbors[0],root,this,false);
				rightChild=new PhyloCluster(root.neighbors[1],root,this,false);
				}
			return;
			}

		System.out.println("Note that the value of root.neighbors.length is "+root.neighbors.length+".");

		name=null;
		originalName=null;
		throw new RuntimeException("Error - bad structure!");

		}//constructor


	/**
	*Returns a String representation of the phylogenetic tree rooted at this cluster.
	*@return toString(0).
	*@see the other toString method of this class!!!!!
	*/
	public String toString()
		{
		return this.toString(0);
		}	

	/**
	*A recusrive function use dto return a String representation of the phylogenetic tree rooted at this cluster.
	*This method calls itself recurisvely.
	*@param depth used to indicate the depth of recursion.  The parameter is also used to insert a number of tabs for easy reading in the String returned
	*@return a String representation of the phylogenetic tree rooted at this cluster.
	*/
	public String toString(int depth)
		{
		String r=new String("");
		if(leftChild==null && rightChild==null)
			{
			r+=strRep("  ",depth)+new String("Leaf Cluster "+name+", distance "+clusterDistance+".\n");
			}
		else
			{
			r+=strRep("  ",depth)+new String("Branch Cluster, Anonymous, distance "+clusterDistance+".\n");
			r+=leftChild.toString(depth+2);
			r+=rightChild.toString(depth+2);
			}
		return r;
		}


	/**
	*Given a string, return a repetition of it.
	*@param s a string to repeat
	*@param r the number of desired repetitions
	*@return s concatenated with itselt r times
	*/
	public static String strRep(String s,int r)
		{
		String q=new String("");
		for(int a=0;a<r;a++)
			{
			q+=s;
			}
		return q;
		}


	private int nameLeftX=(-1);
	private int nameRightX=(-1);
	private int nameBottomY=(-1);
	private int nameTopY=(-1);

	private void storeNamePosition(Graphics2D g,double scale,double lscale)
		{
		FontMetrics fm=g.getFontMetrics();
		AffineTransform at=g.getTransform();
		int transx=(int)(at.getTranslateX());
		int transy=(int)(at.getTranslateY());
		nameLeftX=transx+(int)(scale*lscale*clusterDistance);
		nameBottomY=transy-(fm.getHeight()/2);
		nameTopY=transy+(fm.getHeight()/2);
		nameRightX=nameLeftX+nameLengthInPixels;
		}



	/**
	*Given an x and a y, return this cluster if they are in the bounds of the name, painted.
	*@param x the x coordinate
	*@param y the y coordinate
	*@return this cluster if the x and y are in the bounds of the name, null otherwise.
	*/
	public PhyloCluster isInBounds(int x,int y)
		{
		if(nameLeftX<=x && x<=nameRightX && nameBottomY<=y && y<=nameTopY)
			{
			return this;
			}
		else
			{
			return null;
			}
		}


	/**
	*Given the phylogenetic tree rooted at this cluster and given coordinates (presumably from a mouseclick)
	*return the indicated cluster.  This method is used to find out if a cluster is clicked!
	*This method calls itself recursively in parent-node situations but the isInBounds
	*is used in leaf/nonparent situations.
	*@param x an x coordinate with respect to the drawing/graphics surface
	*@param y a y coordinate with respect to the drawing/graphics surface
	*@return the PhyloCluster indicated/implied by the coordinates or NULL if none is indicated/clicked
	*/
	public PhyloCluster recIsInBounds(int x,int y)
		{
		PhyloCluster clicked=null;
		if(isValidParent())
			{
			clicked=rightChild.recIsInBounds(x,y);
			if(clicked!=null)
				{
				return clicked;
				}
			clicked=leftChild.recIsInBounds(x,y);
			if(clicked!=null)
				{
				return clicked;
				}
			}
		else if(isValidLeaf())
			{
			clicked=isInBounds(x,y);
			}
		return clicked;
		}






	/**
	*Given a graphics context, a scale, height scale, and branch length scale, draw the subtree rooted at this cluster.
	*This method is called recursivly on any children of this cluster!  This method is one of the most complex methods of this class.
	*@param g a graphics context in which to draw
	*@param scale a scale factor used in all drawing
	*@param hval a scale factor used in drawing spacing between subtrees
	*@param lvals a scale factor used in drawing the branch lengths between clusters
	*/
	public void paint(Graphics g,double scale,double hval,double lval)
		{

		//System.out.println("Now painting a cluster!");
		//System.out.println("The graphics object is :"+g.toString()+".");

		Graphics2D g2d=(Graphics2D)(g);
		AffineTransform at=g2d.getTransform();
		double begClusterDistance=clusterDistance;
		clusterDistance=Math.abs(clusterDistance);
		int numProg=getNumProgeny();

		if(!isValidLeaf() && !isValidParent())
			{
			//if it's not a valid parent and it's not a valid leaf, then....
			clusterDistance=begClusterDistance;
			throw new RuntimeException("Error - in paint, a cluster is neither a valid leaf nor a valid parent!");
			}
		else if(isValidLeaf())
			{
			//draw the final stem
			//base case - draw a terminal leaf
			g.setColor(clusterColor);
			if((int)(scale*clusterDistance*lval)<5)
				{
				g.drawLine(0,0,(int)(scale*clusterDistance*lval)+5,0);
				storeNameLength((Graphics2D)(g));
				storeNamePosition((Graphics2D)(g),scale,lval);
				maxx=Math.max(maxx,(int)(at.getTranslateX())+(int)(scale*clusterDistance*lval)+nameLengthInPixels+50);
				g.drawString(name,(int)(scale*clusterDistance*lval)+5,0);
				}
			else
				{
				g.drawLine(0,0,(int)(scale*clusterDistance*lval),0);
				storeNameLength((Graphics2D)(g));
				storeNamePosition((Graphics2D)(g),scale,lval);
				maxx=Math.max(maxx,(int)(at.getTranslateX())+(int)(scale*clusterDistance*lval)+nameLengthInPixels+50);
				g.drawString(name,(int)(scale*clusterDistance*lval)+5,0);
				}
			}//base case
		else
			{
			//is valid parent...
			g.setColor(clusterColor);

			int lineStartx=0;
			int lineStarty=0;
			int lineEndx=(int)(scale*clusterDistance*lval);;
			int lineEndy=0;
			int midx=lineEndx;
			//TOP IS LEFT, BOTTOM IS RIGHT
			int topx=midx;
			int botx=topx;
			int totalLength=(int)
								(
									(double)
										(leftChild.getNumProgeny()+rightChild.getNumProgeny())
									*hval*scale
								);
			double dTotalLength=(double)(totalLength);
			double dTotProgeny=(double)(leftChild.getNumProgeny())+(double)(rightChild.getNumProgeny());
			double loc=(double)(leftChild.getNumProgeny())/dTotProgeny;
			int topy=(int)(dTotalLength-loc*dTotalLength);
			int boty=-(int)(dTotalLength-(double)(topy));

			//draw the initial line
			g.drawLine(lineStartx,lineStarty,lineEndx,lineEndy);

			//do top/left
			g.drawLine(lineEndx,lineEndy,topx,topy);
			g.translate(topx,topy);
			leftChild.paint(g,scale,hval,lval);
			g.setColor(clusterColor);
			g.translate(-topx,-topy);

			//do bottom/right
			g.drawLine(lineEndx,lineEndy,botx,boty);
			g.translate(botx,boty);
			rightChild.paint(g,scale,hval,lval);
			g.setColor(clusterColor);
			g.translate(-botx,-boty);

			//get maxes and mins
			currenttransx=currenttransx+topx;
			currenttransy=currenttransy+topy;
			}//valid parent
		clusterDistance=begClusterDistance;

		if(g instanceof Graphics2D)
			{
			//System.out.println("Currently in a 2D graphics mode:");
			//System.out.println("The transformation is "+at.toString()+".");
			double dx=at.getTranslateX();
			double dy=at.getTranslateY();
			//System.out.println("The x translation is "+dx+".");
			//System.out.println("The y translation is "+dy+".");
			maxx=Math.max(maxx,(int)(dx));
			minx=Math.min(minx,(int)(dx));
			maxy=Math.max(maxy,(int)(dy));
			miny=Math.min(miny,(int)(dy));
			}
		}


	/**
	*Returns true if this cluster is either a valid leaf or if this cluster a valid parent and its children are valid leaves
	*@return true if this cluster is either a valid leaf or if this cluster a valid parent and its children are valid leaves
	*/
	public boolean isValidCluster()
		{
		if(isValidLeaf())
			{
			return true;
			}
		else if(isValidParent() && rightChild.isValidCluster() && leftChild.isValidCluster())
			{
			return true;
			}
		else
			{
			return false;
			}
		}

	/**
	*A cluster is a valid leaf if it has a name length greater than zero and null children.
	*@return true if this cluster is a valid leaf, false otherwise
	*/
	public boolean isValidLeaf()
		{
		if(name.length()>0 && rightChild==null && leftChild==null)
			{
			return true;
			}
		else
			{
			return false;
			}
		}


	/**
	*A cluster is a vlid parent if a) it's not a valid leaf, b) its right child is non-null, and c) its left-child is non-null
	*@return true if this cluster is a valid parent, false otherwise
	*/
	public boolean isValidParent()
		{
		if(!isValidLeaf() && rightChild!=null && leftChild!=null)
			{
			return true;
			}
		else
			{
			return false;
			}
		}

	/**
	*A setter method for this cluster's name
	*@param n the new name for this cluster
	*/
	public void setName(String n)
		{
		name=new String(n);
		}

	/**
	*A getter method for this cluster's name.
	*@return this cluster's name.
	*/
	public String getName()
		{
		return name;
		}


	/**
	*A getter method for this cluster's original name.
	*@return this cluster's original name.
	*/
	public String getOriginalName()
		{
		return new String(originalName);
		}


	/**
	*Including this cluster, return the number of cluster in the subtree rooted at this cluster.
	*@return the number of cluster in the subtree rooted at this cluster.
	*/
	public int getNumProgeny()
		{
		int leftProg=0;
		int rightProg=0;

		if(leftChild!=null)
			{
			leftProg=leftChild.getNumProgeny();
			}
		if(rightChild!=null)
			{
			rightProg=rightChild.getNumProgeny();
			}
		return Math.max(1,leftProg+rightProg);
		}
	}
