/*
Copyright (C) 2009 Diego Darriba
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 program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package es.uvigo.darwin.prottest.tree;
import pal.tree.Node;
import pal.tree.Tree;
import es.uvigo.darwin.prottest.util.exception.ProtTestInternalException;
import es.uvigo.darwin.prottest.util.printer.ProtTestFormattedOutput;
import java.io.PrintWriter;
import java.io.StringWriter;
import pal.io.FormattedOutput;
import pal.misc.IdGroup;
import pal.misc.Identifier;
import pal.misc.SimpleIdGroup;
import pal.tree.NodeUtils;
/**
* Some utils to phylogenetic tree analysis.
* Based on PAL library.
*/
public abstract class TreeUtils {
public static final int DEFAULT_COLUMN_WIDTH = 70;
public static final String TREE_WEIGHT_ATTRIBUTE = "weight";
public static final String TREE_CLADE_SUPPORT_ATTRIBUTE = "support";
public static final String TREE_NAME_ATTRIBUTE = "treeName";
/**
* Calculates the euclidean tree distance between two trees.
*
* @param t1 the first tree
* @param t2 the second tree
*
* @return the double
*/
public static double euclideanTreeDistance(Tree t1, Tree t2) {
double sum = 0.0;
int numberOfInternalNodes = t1.getInternalNodeCount();
if (numberOfInternalNodes != t2.getInternalNodeCount()) {
throw new ProtTestInternalException("Different number of internal nodes: " +
t1.getInternalNodeCount() + " vs " + t2.getInternalNodeCount());
}
int numberOfExternalNodes = t1.getExternalNodeCount();
if (numberOfExternalNodes != t2.getExternalNodeCount()) {
throw new ProtTestInternalException("Different number of external nodes: " +
t1.getInternalNodeCount() + " vs " + t2.getInternalNodeCount());
}
for (int i = 0; i < numberOfInternalNodes; i++) {
double bl1 = t1.getInternalNode(i).getBranchLength();
double bl2 = t2.getInternalNode(i).getBranchLength();
sum += (bl1 - bl2) * (bl1 - bl2);
}
for (int i = 0; i < numberOfExternalNodes; i++) {
double bl1 = t1.getExternalNode(i).getBranchLength();
double bl2 = t2.getExternalNode(i).getBranchLength();
sum += (bl1 - bl2) * (bl1 - bl2);
}
return Math.sqrt(sum);
}
/**
* Calculates the Robinson-Foulds tree distance between two trees.
*
* @param t1 the first tree
* @param t2 the second tree
*
* @return the double
*/
public static double robinsonFouldsTreeDistance(Tree t1, Tree t2) {
return pal.tree.TreeUtils.getRobinsonFouldsDistance(t1, t2);
}
//
/**
* Calculates the number of branches from node to most remote tip.
*
* @param node the starting node
*
* @return the node distance
*/
public static int nodeDistance(final Node node) {
if (node.isLeaf()) {
return 0;
}
int d = 0;
for (int i = 0; i < node.getChildCount(); i++) {
Node n = node.getChild(i);
d = Math.max(d, nodeDistance(n));
}
return d + 1;
}
/**
* Calculates the safe node height.
*
* @param tree the tree
* @param node the node
*
* @return the height of the node
*/
public static double safeNodeHeight(final Tree tree, final Node node) {
if (node.getNodeHeight() > 0.0) {
return node.getNodeHeight();
}
return TreeUtils.nodeDistance(node);
}
/**
* Make sure subtree below node has consistent heights, i.e. node height is higher than it's descendants
*
* @param tree the tree
* @param node the node
*
* @return height of node
*/
public static double insureConsistency(Tree tree, Node node) {
double height = TreeUtils.safeNodeHeight(tree, node);
if (node.isLeaf()) {
return height;
} else {
for (int i = 0; i < node.getChildCount(); i++) {
Node n = node.getChild(i);
final double childHeight = insureConsistency(tree, n);
height = Math.max(height, childHeight);
}
}
node.setNodeHeight(height);
return height;
}
@Deprecated
public static int printNH(PrintWriter out, Tree tree,
boolean printLengths, boolean printInternalLabels,
boolean printCladeSupport) {
return printNH(out, tree, tree.getRoot(),
printLengths, printInternalLabels,
printCladeSupport,
0, true, DEFAULT_COLUMN_WIDTH);
}
public static String toNewick(Tree tree, boolean printLengths,
boolean printInternalLabels, boolean printCladeSupport) {
StringWriter sw = new StringWriter();
PrintWriter mp = new PrintWriter(sw);
printNH(mp, tree, tree.getRoot(),
printLengths, printInternalLabels, printCladeSupport,
0, false, -1);
sw.append(';');
return sw.toString();
}
@Deprecated
public static int printNH(PrintWriter out, Tree tree, Node node,
boolean printLengths, boolean printInternalLabels,
boolean printCladeSupport,
int column, boolean breakLines, int colWidth) {
if (breakLines) {
column = breakLine(out, column, colWidth);
}
if (!node.isLeaf()) {
out.print("(");
column++;
for (int i = 0; i < node.getChildCount(); i++) {
if (i != 0) {
out.print(",");
column++;
}
column = printNH(out, tree, node.getChild(i),
printLengths, printInternalLabels,
printCladeSupport,
column, breakLines, colWidth);
}
out.print(")");
column++;
}
if (!node.isRoot()) {
if (node.isLeaf() || printInternalLabels) {
if (breakLines) {
column = breakLine(out, column, colWidth);
}
String id = node.getIdentifier().toString();
out.print(id);
column += id.length();
}
if (printCladeSupport) {
if (tree.getAttribute(node, TREE_CLADE_SUPPORT_ATTRIBUTE) != null) {
double support = (Double) tree.getAttribute(node, TREE_CLADE_SUPPORT_ATTRIBUTE);
column += ProtTestFormattedOutput.displayDecimal(out, support, 2);
}
}
if (printLengths) {
out.print(":");
column++;
if (breakLines) {
column = breakLine(out, column, colWidth);
}
column += ProtTestFormattedOutput.displayDecimal(out, node.getBranchLength(), 6);
}
}
return column;
}
/**
* get list of the identifiers of the external nodes
*
* @return leaf identifier group
*/
public static final IdGroup getLeafIdGroup(Tree tree) {
tree.createNodeList();
IdGroup labelList =
new SimpleIdGroup(tree.getExternalNodeCount());
for (int i = 0; i < tree.getExternalNodeCount(); i++) {
labelList.setIdentifier(i, tree.getExternalNode(i).getIdentifier());
}
return labelList;
}
private static int breakLine(PrintWriter out, int column, int colWidth) {
if (column > colWidth) {
out.println();
column = 0;
}
return column;
}
// Print picture of current tree in ASCII
public static void printASCII(Tree tree, PrintWriter out) {
FormattedOutput format = FormattedOutput.getInstance();
tree.createNodeList();
int numExternalNodes = tree.getExternalNodeCount();
int numInternalNodes = tree.getInternalNodeCount();
int numBranches = numInternalNodes + numExternalNodes - 1;
boolean[] umbrella = new boolean[numExternalNodes];
int[] position = new int[numExternalNodes];
int minLength = (Integer.toString(numBranches)).length() + 1;
int MAXCOLUMN = 40;
Node root = tree.getRoot();
if (root.getNodeHeight() == 0.0) {
NodeUtils.lengths2Heights(root);
}
double proportion = (double) MAXCOLUMN / root.getNodeHeight();
for (int n = 0; n < numExternalNodes; n++) {
umbrella[n] = false;
}
position[0] = 1;
for (int i = root.getChildCount() - 1; i > -1; i--) {
printNodeInASCII(out, root.getChild(i), 1, i, root.getChildCount(),
numExternalNodes, umbrella, position, proportion, minLength);
if (i != 0) {
putCharAtLevel(out, 0, '|', position);
out.println();
}
}
}
private static void printNodeInASCII(PrintWriter out, Node node, int level, int m, int maxm,
int numExternalNodes, boolean[] umbrella, int[] position, double proportion, int minLength) {
position[level] = (int) (node.getBranchLength() * proportion);
if (position[level] < minLength) {
position[level] = minLength;
}
if (node.isLeaf()) // external branch
{
if (m == maxm - 1) {
umbrella[level - 1] = true;
}
printlnNodeWithNumberAndLabel(out, node, level, numExternalNodes, umbrella, position);
if (m == 0) {
umbrella[level - 1] = false;
}
} else // internal branch
{
for (int n = node.getChildCount() - 1; n > -1; n--) {
printNodeInASCII(out, node.getChild(n), level + 1, n, node.getChildCount(),
numExternalNodes, umbrella, position, proportion, minLength);
if (m == maxm - 1 && n == node.getChildCount() / 2) {
umbrella[level - 1] = true;
}
if (n != 0) {
if (n == node.getChildCount() / 2) {
printlnNodeWithNumberAndLabel(out, node, level, numExternalNodes, umbrella, position);
} else {
for (int i = 0; i < level + 1; i++) {
if (umbrella[i]) {
putCharAtLevel(out, i, '|', position);
} else {
putCharAtLevel(out, i, ' ', position);
}
}
out.println();
}
}
if (m == 0 && n == node.getChildCount() / 2) {
umbrella[level - 1] = false;
}
}
}
}
private static void printlnNodeWithNumberAndLabel(PrintWriter out, Node node, int level,
int numExternalNodes, boolean[] umbrella, int[] position) {
for (int i = 0; i < level - 1; i++) {
if (umbrella[i]) {
putCharAtLevel(out, i, '|', position);
} else {
putCharAtLevel(out, i, ' ', position);
}
}
putCharAtLevel(out, level - 1, '+', position);
int branchNumber;
if (node.isLeaf()) {
branchNumber = node.getNumber() + 1;
} else {
branchNumber = node.getNumber() + 1 + numExternalNodes;
}
String numberAsString = Integer.toString(branchNumber);
int numDashs = position[level] - numberAsString.length();
// String cladeSupport = "";
// if (node instanceof AttributeNode && ((AttributeNode)node).getAttribute(TreeUtils.TREE_CLADE_SUPPORT_ATTRIBUTE) != null) {
// double cladeSupportValue = (Double) ((AttributeNode)node).getAttribute(TreeUtils.TREE_CLADE_SUPPORT_ATTRIBUTE);
// cladeSupport = ProtTestFormattedOutput.getDecimalString(cladeSupportValue, 1);
// }
//
// numDashs -= cladeSupport.length();
for (int i = 0; i < numDashs ; i++) {
out.print('-');
}
out.print(numberAsString);
if (node.isLeaf()) {
out.println(" " + node.getIdentifier());
} else {
if (!node.getIdentifier().equals(Identifier.ANONYMOUS)) {
out.print("(" + node.getIdentifier() + ")");
}
out.println();
}
}
private static void putCharAtLevel(PrintWriter out, int level, char c,
int[] position) {
int n = position[level] - 1;
for (int i = 0; i < n; i++) {
out.print(' ');
}
out.print(c);
}
public static void printBranchInfo(Tree tree, PrintWriter out) {
//
// CALL PRINTASCII FIRST !!!
//
// check if some SE values differ from the default zero
int numExternalNodes = tree.getExternalNodeCount();
int numInternalNodes = tree.getInternalNodeCount();
int numBranches = numBranches = numInternalNodes + numExternalNodes - 1;
boolean showSE = false;
for (int i = 0; i < numExternalNodes && showSE == false; i++) {
if (tree.getExternalNode(i).getBranchLengthSE() != 0.0) {
showSE = true;
}
if (i < numInternalNodes - 1) {
if (tree.getInternalNode(i).getBranchLengthSE() != 0.0) {
showSE = true;
}
}
}
ProtTestFormattedOutput.displayIntegerWhite(out, numExternalNodes);
out.print(" Length ");
if (showSE) {
out.print("S.E. ");
}
out.print("Label ");
if (numInternalNodes > 1) {
ProtTestFormattedOutput.displayIntegerWhite(out, numBranches);
out.print(" Length ");
if (showSE) {
out.print("S.E. ");
}
out.print("Label");
}
out.println();
for (int i = 0; i < numExternalNodes; i++) {
ProtTestFormattedOutput.displayInteger(out, i + 1, numExternalNodes);
out.print(" ");
ProtTestFormattedOutput.displayDecimal(out, tree.getExternalNode(i).getBranchLength(), 5);
out.print(" ");
if (showSE) {
ProtTestFormattedOutput.displayDecimal(out, tree.getExternalNode(i).getBranchLengthSE(), 5);
out.print(" ");
}
ProtTestFormattedOutput.displayLabel(out, tree.getExternalNode(i).getIdentifier().getName(), 10);
if (i < numInternalNodes - 1) {
ProtTestFormattedOutput.multiplePrint(out, ' ', 5);
ProtTestFormattedOutput.displayInteger(out, i + 1 + numExternalNodes, numBranches);
out.print(" ");
ProtTestFormattedOutput.displayDecimal(out, tree.getInternalNode(i).getBranchLength(), 5);
out.print(" ");
if (showSE) {
ProtTestFormattedOutput.displayDecimal(out, tree.getInternalNode(i).getBranchLengthSE(), 5);
out.print(" ");
}
ProtTestFormattedOutput.displayLabel(out, tree.getInternalNode(i).getIdentifier().getName(), 10);
}
out.println();
}
}
// Print height information
public static void heightInfo(Tree tree, PrintWriter out) {
int numExternalNodes = tree.getExternalNodeCount();
int numInternalNodes = tree.getInternalNodeCount();
int numBranches = numExternalNodes + numInternalNodes - 1;
if (tree.getRoot().getNodeHeight() == 0.0) {
NodeUtils.lengths2Heights(tree.getRoot());
}
ProtTestFormattedOutput.displayIntegerWhite(out, numExternalNodes);
out.print(" Height ");
ProtTestFormattedOutput.displayIntegerWhite(out, numBranches);
out.print(" Height ");
out.println();
for (int i = 0; i < numExternalNodes; i++) {
ProtTestFormattedOutput.displayInteger(out, i + 1, numExternalNodes);
out.print(" ");
ProtTestFormattedOutput.displayDecimal(out, tree.getExternalNode(i).getNodeHeight(), 7);
out.print(" ");
if (i < numInternalNodes) {
ProtTestFormattedOutput.multiplePrint(out, ' ', 5);
if (i == numInternalNodes - 1) {
out.print("R");
ProtTestFormattedOutput.multiplePrint(out, ' ', Integer.toString(numBranches).length() - 1);
} else {
ProtTestFormattedOutput.displayInteger(out, i + 1 + numExternalNodes, numBranches);
}
out.print(" ");
ProtTestFormattedOutput.displayDecimal(out, tree.getInternalNode(i).getNodeHeight(), 7);
out.print(" ");
}
out.println();
}
}
}