inprogress
[jalview.git] / forester / java / src / org / forester / phylogeny / PhylogenyMethods.java
index 09db386..01c84c3 100644 (file)
@@ -21,7 +21,7 @@
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
 //
 // Contact: phylosoft @ gmail . com
-// WWW: www.phylosoft.org/forester
+// WWW: https://sites.google.com/site/cmzmasek/home/software/forester
 
 package org.forester.phylogeny;
 
@@ -36,14 +36,18 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
-import java.util.SortedMap;
-import java.util.TreeMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
+import org.forester.io.parsers.FastaParser;
 import org.forester.io.parsers.PhylogenyParser;
 import org.forester.io.parsers.phyloxml.PhyloXmlDataFormatException;
 import org.forester.io.parsers.phyloxml.PhyloXmlUtil;
 import org.forester.io.parsers.util.PhylogenyParserException;
+import org.forester.phylogeny.data.Accession;
+import org.forester.phylogeny.data.Annotation;
 import org.forester.phylogeny.data.BranchColor;
 import org.forester.phylogeny.data.BranchWidth;
 import org.forester.phylogeny.data.Confidence;
@@ -58,74 +62,47 @@ import org.forester.phylogeny.factories.PhylogenyFactory;
 import org.forester.phylogeny.iterators.PhylogenyNodeIterator;
 import org.forester.util.BasicDescriptiveStatistics;
 import org.forester.util.DescriptiveStatistics;
-import org.forester.util.FailedConditionCheckException;
 import org.forester.util.ForesterUtil;
 
 public class PhylogenyMethods {
 
-    private static PhylogenyMethods _instance   = null;
-    private PhylogenyNode           _farthest_1 = null;
-    private PhylogenyNode           _farthest_2 = null;
-
     private PhylogenyMethods() {
         // Hidden constructor.
     }
 
-    /**
-     * Calculates the distance between PhylogenyNodes node1 and node2.
-     * 
-     * 
-     * @param node1
-     * @param node2
-     * @return distance between node1 and node2
-     */
-    public double calculateDistance( final PhylogenyNode node1, final PhylogenyNode node2 ) {
-        final PhylogenyNode lca = calculateLCA( node1, node2 );
-        final PhylogenyNode n1 = node1;
-        final PhylogenyNode n2 = node2;
-        return ( PhylogenyMethods.getDistance( n1, lca ) + PhylogenyMethods.getDistance( n2, lca ) );
-    }
-
-    public double calculateFurthestDistance( final Phylogeny phylogeny ) {
-        if ( phylogeny.getNumberOfExternalNodes() < 2 ) {
-            return 0.0;
-        }
-        _farthest_1 = null;
-        _farthest_2 = null;
-        PhylogenyNode node_1 = null;
-        PhylogenyNode node_2 = null;
-        double farthest_d = -Double.MAX_VALUE;
-        final PhylogenyMethods methods = PhylogenyMethods.getInstance();
-        final List<PhylogenyNode> ext_nodes = phylogeny.getRoot().getAllExternalDescendants();
-        for( int i = 1; i < ext_nodes.size(); ++i ) {
-            for( int j = 0; j < i; ++j ) {
-                final double d = methods.calculateDistance( ext_nodes.get( i ), ext_nodes.get( j ) );
-                if ( d < 0.0 ) {
-                    throw new RuntimeException( "distance cannot be negative" );
-                }
-                if ( d > farthest_d ) {
-                    farthest_d = d;
-                    node_1 = ext_nodes.get( i );
-                    node_2 = ext_nodes.get( j );
-                }
-            }
-        }
-        _farthest_1 = node_1;
-        _farthest_2 = node_2;
-        return farthest_d;
-    }
-
     @Override
     public Object clone() throws CloneNotSupportedException {
         throw new CloneNotSupportedException();
     }
 
-    public PhylogenyNode getFarthestNode1() {
-        return _farthest_1;
-    }
-
-    public PhylogenyNode getFarthestNode2() {
-        return _farthest_2;
+    public static boolean extractFastaInformation( final Phylogeny phy ) {
+        boolean could_extract = false;
+        for( final PhylogenyNodeIterator iter = phy.iteratorExternalForward(); iter.hasNext(); ) {
+            final PhylogenyNode node = iter.next();
+            if ( !ForesterUtil.isEmpty( node.getName() ) ) {
+                final Matcher name_m = FastaParser.FASTA_DESC_LINE.matcher( node.getName() );
+                if ( name_m.lookingAt() ) {
+                    could_extract = true;
+                    final String acc_source = name_m.group( 1 );
+                    final String acc = name_m.group( 2 );
+                    final String seq_name = name_m.group( 3 );
+                    final String tax_sn = name_m.group( 4 );
+                    if ( !ForesterUtil.isEmpty( acc_source ) && !ForesterUtil.isEmpty( acc ) ) {
+                        ForesterUtil.ensurePresenceOfSequence( node );
+                        node.getNodeData().getSequence( 0 ).setAccession( new Accession( acc, acc_source ) );
+                    }
+                    if ( !ForesterUtil.isEmpty( seq_name ) ) {
+                        ForesterUtil.ensurePresenceOfSequence( node );
+                        node.getNodeData().getSequence( 0 ).setName( seq_name );
+                    }
+                    if ( !ForesterUtil.isEmpty( tax_sn ) ) {
+                        ForesterUtil.ensurePresenceOfTaxonomy( node );
+                        node.getNodeData().getTaxonomy( 0 ).setScientificName( tax_sn );
+                    }
+                }
+            }
+        }
+        return could_extract;
     }
 
     public static DescriptiveStatistics calculatBranchLengthStatistics( final Phylogeny phy ) {
@@ -168,6 +145,21 @@ public class PhylogenyMethods {
     }
 
     /**
+     * Calculates the distance between PhylogenyNodes node1 and node2.
+     * 
+     * 
+     * @param node1
+     * @param node2
+     * @return distance between node1 and node2
+     */
+    public static double calculateDistance( final PhylogenyNode node1, final PhylogenyNode node2 ) {
+        final PhylogenyNode lca = calculateLCA( node1, node2 );
+        final PhylogenyNode n1 = node1;
+        final PhylogenyNode n2 = node2;
+        return ( PhylogenyMethods.getDistance( n1, lca ) + PhylogenyMethods.getDistance( n2, lca ) );
+    }
+
+    /**
      * Returns the LCA of PhylogenyNodes node1 and node2.
      * 
      * 
@@ -308,6 +300,29 @@ public class PhylogenyMethods {
         return stats;
     }
 
+    public final static void collapseSubtreeStructure( final PhylogenyNode n ) {
+        final List<PhylogenyNode> eds = n.getAllExternalDescendants();
+        final List<Double> d = new ArrayList<Double>();
+        for( final PhylogenyNode ed : eds ) {
+            d.add( calculateDistanceToAncestor( n, ed ) );
+        }
+        for( int i = 0; i < eds.size(); ++i ) {
+            n.setChildNode( i, eds.get( i ) );
+            eds.get( i ).setDistanceToParent( d.get( i ) );
+        }
+    }
+
+    public static int countNumberOfOneDescendantNodes( final Phylogeny phy ) {
+        int count = 0;
+        for( final PhylogenyNodeIterator iter = phy.iteratorPreorder(); iter.hasNext(); ) {
+            final PhylogenyNode n = iter.next();
+            if ( !n.isExternal() && ( n.getNumberOfDescendants() == 1 ) ) {
+                count++;
+            }
+        }
+        return count;
+    }
+
     public static int countNumberOfPolytomies( final Phylogeny phy ) {
         int count = 0;
         for( final PhylogenyNodeIterator iter = phy.iteratorPreorder(); iter.hasNext(); ) {
@@ -321,16 +336,15 @@ public class PhylogenyMethods {
 
     public static final HashMap<String, PhylogenyNode> createNameToExtNodeMap( final Phylogeny phy ) {
         final HashMap<String, PhylogenyNode> nodes = new HashMap<String, PhylogenyNode>();
-        for( final PhylogenyNodeIterator iter = phy.iteratorExternalForward(); iter.hasNext(); ) {
-            final PhylogenyNode n = iter.next();
+        final List<PhylogenyNode> ext = phy.getExternalNodes();
+        for( final PhylogenyNode n : ext ) {
             nodes.put( n.getName(), n );
         }
         return nodes;
     }
 
-    public static void deleteExternalNodesNegativeSelection( final Set<Integer> to_delete, final Phylogeny phy ) {
-        phy.clearHashIdToNodeMap();
-        for( final Integer id : to_delete ) {
+    public static void deleteExternalNodesNegativeSelection( final Set<Long> to_delete, final Phylogeny phy ) {
+        for( final Long id : to_delete ) {
             phy.deleteSubtree( phy.getNode( id ), true );
         }
         phy.clearHashIdToNodeMap();
@@ -358,24 +372,6 @@ public class PhylogenyMethods {
         p.externalNodesHaveChanged();
     }
 
-    public static void deleteExternalNodesPositiveSelection( final Set<Taxonomy> species_to_keep, final Phylogeny phy ) {
-        //   final Set<Integer> to_delete = new HashSet<Integer>();
-        for( final PhylogenyNodeIterator it = phy.iteratorExternalForward(); it.hasNext(); ) {
-            final PhylogenyNode n = it.next();
-            if ( n.getNodeData().isHasTaxonomy() ) {
-                if ( !species_to_keep.contains( n.getNodeData().getTaxonomy() ) ) {
-                    //to_delete.add( n.getNodeId() );
-                    phy.deleteSubtree( n, true );
-                }
-            }
-            else {
-                throw new IllegalArgumentException( "node " + n.getId() + " has no taxonomic data" );
-            }
-        }
-        phy.clearHashIdToNodeMap();
-        phy.externalNodesHaveChanged();
-    }
-
     public static List<String> deleteExternalNodesPositiveSelection( final String[] node_names_to_keep,
                                                                      final Phylogeny p ) {
         final PhylogenyNodeIterator it = p.iteratorExternalForward();
@@ -398,6 +394,37 @@ public class PhylogenyMethods {
         return deleted;
     }
 
+    public static void deleteExternalNodesPositiveSelectionT( final List<Taxonomy> species_to_keep, final Phylogeny phy ) {
+        final Set<Long> to_delete = new HashSet<Long>();
+        for( final PhylogenyNodeIterator it = phy.iteratorExternalForward(); it.hasNext(); ) {
+            final PhylogenyNode n = it.next();
+            if ( n.getNodeData().isHasTaxonomy() ) {
+                if ( !species_to_keep.contains( n.getNodeData().getTaxonomy() ) ) {
+                    to_delete.add( n.getId() );
+                }
+            }
+            else {
+                throw new IllegalArgumentException( "node " + n.getId() + " has no taxonomic data" );
+            }
+        }
+        deleteExternalNodesNegativeSelection( to_delete, phy );
+    }
+
+    final public static void deleteInternalNodesWithOnlyOneDescendent( final Phylogeny phy ) {
+        final ArrayList<PhylogenyNode> to_delete = new ArrayList<PhylogenyNode>();
+        for( final PhylogenyNodeIterator iter = phy.iteratorPostorder(); iter.hasNext(); ) {
+            final PhylogenyNode n = iter.next();
+            if ( ( !n.isExternal() ) && ( n.getNumberOfDescendants() == 1 ) ) {
+                to_delete.add( n );
+            }
+        }
+        for( final PhylogenyNode d : to_delete ) {
+            PhylogenyMethods.removeNode( d, phy );
+        }
+        phy.clearHashIdToNodeMap();
+        phy.externalNodesHaveChanged();
+    }
+
     final public static void deleteNonOrthologousExternalNodes( final Phylogeny phy, final PhylogenyNode n ) {
         if ( n.isInternal() ) {
             throw new IllegalArgumentException( "node is not external" );
@@ -416,9 +443,29 @@ public class PhylogenyMethods {
         phy.externalNodesHaveChanged();
     }
 
+    public final static List<List<PhylogenyNode>> divideIntoSubTrees( final Phylogeny phy,
+                                                                      final double min_distance_to_root ) {
+        if ( min_distance_to_root <= 0 ) {
+            throw new IllegalArgumentException( "attempt to use min distance to root of: " + min_distance_to_root );
+        }
+        final List<List<PhylogenyNode>> l = new ArrayList<List<PhylogenyNode>>();
+        setAllIndicatorsToZero( phy );
+        for( final PhylogenyNodeIterator it = phy.iteratorExternalForward(); it.hasNext(); ) {
+            final PhylogenyNode n = it.next();
+            if ( n.getIndicator() != 0 ) {
+                continue;
+            }
+            l.add( divideIntoSubTreesHelper( n, min_distance_to_root ) );
+            if ( l.isEmpty() ) {
+                throw new RuntimeException( "this should not have happened" );
+            }
+        }
+        return l;
+    }
+
     public static List<PhylogenyNode> getAllDescendants( final PhylogenyNode node ) {
         final List<PhylogenyNode> descs = new ArrayList<PhylogenyNode>();
-        final Set<Integer> encountered = new HashSet<Integer>();
+        final Set<Long> encountered = new HashSet<Long>();
         if ( !node.isExternal() ) {
             final List<PhylogenyNode> exts = node.getAllExternalDescendants();
             for( PhylogenyNode current : exts ) {
@@ -524,13 +571,12 @@ public class PhylogenyMethods {
         return farthest;
     }
 
-    public static PhylogenyMethods getInstance() {
-        if ( PhylogenyMethods._instance == null ) {
-            PhylogenyMethods._instance = new PhylogenyMethods();
-        }
-        return PhylogenyMethods._instance;
-    }
-
+    // public static PhylogenyMethods getInstance() {
+    //     if ( PhylogenyMethods._instance == null ) {
+    //         PhylogenyMethods._instance = new PhylogenyMethods();
+    //    }
+    //    return PhylogenyMethods._instance;
+    //  }
     /**
      * Returns the largest confidence value found on phy.
      */
@@ -640,36 +686,44 @@ public class PhylogenyMethods {
     }
 
     public static void midpointRoot( final Phylogeny phylogeny ) {
-        if ( phylogeny.getNumberOfExternalNodes() < 2 ) {
+        if ( ( phylogeny.getNumberOfExternalNodes() < 2 ) || ( calculateMaxDistanceToRoot( phylogeny ) <= 0 ) ) {
             return;
         }
-        final PhylogenyMethods methods = getInstance();
-        final double farthest_d = methods.calculateFurthestDistance( phylogeny );
-        final PhylogenyNode f1 = methods.getFarthestNode1();
-        final PhylogenyNode f2 = methods.getFarthestNode2();
-        if ( farthest_d <= 0.0 ) {
-            return;
-        }
-        double x = farthest_d / 2.0;
-        PhylogenyNode n = f1;
-        if ( PhylogenyMethods.getDistance( f1, phylogeny.getRoot() ) < PhylogenyMethods.getDistance( f2, phylogeny
-                .getRoot() ) ) {
-            n = f2;
-        }
-        while ( ( x > n.getDistanceToParent() ) && !n.isRoot() ) {
-            x -= ( n.getDistanceToParent() > 0 ? n.getDistanceToParent() : 0 );
-            n = n.getParent();
+        int counter = 0;
+        final int total_nodes = phylogeny.getNodeCount();
+        while ( true ) {
+            if ( ++counter > total_nodes ) {
+                throw new RuntimeException( "this should not have happened: midpoint rooting does not converge" );
+            }
+            PhylogenyNode a = null;
+            double da = 0;
+            double db = 0;
+            for( int i = 0; i < phylogeny.getRoot().getNumberOfDescendants(); ++i ) {
+                final PhylogenyNode f = getFurthestDescendant( phylogeny.getRoot().getChildNode( i ) );
+                final double df = getDistance( f, phylogeny.getRoot() );
+                if ( df > 0 ) {
+                    if ( df > da ) {
+                        db = da;
+                        da = df;
+                        a = f;
+                    }
+                    else if ( df > db ) {
+                        db = df;
+                    }
+                }
+            }
+            final double diff = da - db;
+            if ( diff < 0.000001 ) {
+                break;
+            }
+            double x = da - ( diff / 2.0 );
+            while ( ( x > a.getDistanceToParent() ) && !a.isRoot() ) {
+                x -= ( a.getDistanceToParent() > 0 ? a.getDistanceToParent() : 0 );
+                a = a.getParent();
+            }
+            phylogeny.reRoot( a, x );
         }
-        phylogeny.reRoot( n, x );
         phylogeny.recalculateNumberOfExternalDescendants( true );
-        final PhylogenyNode a = getFurthestDescendant( phylogeny.getRoot().getChildNode1() );
-        final PhylogenyNode b = getFurthestDescendant( phylogeny.getRoot().getChildNode2() );
-        final double da = getDistance( a, phylogeny.getRoot() );
-        final double db = getDistance( b, phylogeny.getRoot() );
-        if ( Math.abs( da - db ) > 0.000001 ) {
-            throw new FailedConditionCheckException( "this should not have happened: midpoint rooting failed:  da="
-                    + da + ",  db=" + db + ",  diff=" + Math.abs( da - db ) );
-        }
     }
 
     public static void normalizeBootstrapValues( final Phylogeny phylogeny,
@@ -703,34 +757,15 @@ public class PhylogenyMethods {
     }
 
     /**
-     * Returns the set of distinct taxonomies of
-     * all external nodes of node.
-     * If at least one the external nodes has no taxonomy,
-     * null is returned.
-     * 
-     */
-    public static Set<Taxonomy> obtainDistinctTaxonomies( final PhylogenyNode node ) {
-        final List<PhylogenyNode> descs = node.getAllExternalDescendants();
-        final Set<Taxonomy> tax_set = new HashSet<Taxonomy>();
-        for( final PhylogenyNode n : descs ) {
-            if ( !n.getNodeData().isHasTaxonomy() || n.getNodeData().getTaxonomy().isEmpty() ) {
-                return null;
-            }
-            tax_set.add( n.getNodeData().getTaxonomy() );
-        }
-        return tax_set;
-    }
-
-    /**
      * Returns a map of distinct taxonomies of
      * all external nodes of node.
      * If at least one of the external nodes has no taxonomy,
      * null is returned.
      * 
      */
-    public static SortedMap<Taxonomy, Integer> obtainDistinctTaxonomyCounts( final PhylogenyNode node ) {
+    public static Map<Taxonomy, Integer> obtainDistinctTaxonomyCounts( final PhylogenyNode node ) {
         final List<PhylogenyNode> descs = node.getAllExternalDescendants();
-        final SortedMap<Taxonomy, Integer> tax_map = new TreeMap<Taxonomy, Integer>();
+        final Map<Taxonomy, Integer> tax_map = new HashMap<Taxonomy, Integer>();
         for( final PhylogenyNode n : descs ) {
             if ( !n.getNodeData().isHasTaxonomy() || n.getNodeData().getTaxonomy().isEmpty() ) {
                 return null;
@@ -821,7 +856,7 @@ public class PhylogenyMethods {
             return;
         }
         phy.setIdToNodeMap( null );
-        int i = PhylogenyNode.getNodeCount();
+        long i = PhylogenyNode.getNodeCount();
         for( final PhylogenyNodeIterator it = phy.iteratorPreorder(); it.hasNext(); ) {
             it.next().setId( i++ );
         }
@@ -853,9 +888,19 @@ public class PhylogenyMethods {
 
     public static void removeNode( final PhylogenyNode remove_me, final Phylogeny phylogeny ) {
         if ( remove_me.isRoot() ) {
-            throw new IllegalArgumentException( "ill advised attempt to remove root node" );
+            if ( remove_me.getNumberOfDescendants() == 1 ) {
+                final PhylogenyNode desc = remove_me.getDescendants().get( 0 );
+                desc.setDistanceToParent( addPhylogenyDistances( remove_me.getDistanceToParent(),
+                                                                 desc.getDistanceToParent() ) );
+                desc.setParent( null );
+                phylogeny.setRoot( desc );
+                phylogeny.clearHashIdToNodeMap();
+            }
+            else {
+                throw new IllegalArgumentException( "attempt to remove a root node with more than one descendants" );
+            }
         }
-        if ( remove_me.isExternal() ) {
+        else if ( remove_me.isExternal() ) {
             phylogeny.deleteSubtree( remove_me, false );
             phylogeny.clearHashIdToNodeMap();
             phylogeny.externalNodesHaveChanged();
@@ -927,6 +972,10 @@ public class PhylogenyMethods {
                 match = true;
             }
             if ( !match && node.getNodeData().isHasSequence()
+                    && match( node.getNodeData().getSequence().getGeneName(), query, case_sensitive, partial ) ) {
+                match = true;
+            }
+            if ( !match && node.getNodeData().isHasSequence()
                     && match( node.getNodeData().getSequence().getSymbol(), query, case_sensitive, partial ) ) {
                 match = true;
             }
@@ -949,6 +998,38 @@ public class PhylogenyMethods {
                     }
                 }
             }
+            //
+            if ( !match && node.getNodeData().isHasSequence()
+                    && ( node.getNodeData().getSequence().getAnnotations() != null ) ) {
+                for( final Annotation ann : node.getNodeData().getSequence().getAnnotations() ) {
+                    if ( match( ann.getDesc(), query, case_sensitive, partial ) ) {
+                        match = true;
+                        break;
+                    }
+                    if ( match( ann.getRef(), query, case_sensitive, partial ) ) {
+                        match = true;
+                        break;
+                    }
+                }
+            }
+            if ( !match && node.getNodeData().isHasSequence()
+                    && ( node.getNodeData().getSequence().getCrossReferences() != null ) ) {
+                for( final Accession x : node.getNodeData().getSequence().getCrossReferences() ) {
+                    if ( match( x.getComment(), query, case_sensitive, partial ) ) {
+                        match = true;
+                        break;
+                    }
+                    if ( match( x.getSource(), query, case_sensitive, partial ) ) {
+                        match = true;
+                        break;
+                    }
+                    if ( match( x.getValue(), query, case_sensitive, partial ) ) {
+                        match = true;
+                        break;
+                    }
+                }
+            }
+            //
             if ( !match && ( node.getNodeData().getBinaryCharacters() != null ) ) {
                 Iterator<String> it = node.getNodeData().getBinaryCharacters().getPresentCharacters().iterator();
                 I: while ( it.hasNext() ) {
@@ -1027,6 +1108,10 @@ public class PhylogenyMethods {
                     match = true;
                 }
                 if ( !match && node.getNodeData().isHasSequence()
+                        && match( node.getNodeData().getSequence().getGeneName(), query, case_sensitive, partial ) ) {
+                    match = true;
+                }
+                if ( !match && node.getNodeData().isHasSequence()
                         && match( node.getNodeData().getSequence().getSymbol(), query, case_sensitive, partial ) ) {
                     match = true;
                 }
@@ -1049,6 +1134,38 @@ public class PhylogenyMethods {
                         }
                     }
                 }
+                //
+                if ( !match && node.getNodeData().isHasSequence()
+                        && ( node.getNodeData().getSequence().getAnnotations() != null ) ) {
+                    for( final Annotation ann : node.getNodeData().getSequence().getAnnotations() ) {
+                        if ( match( ann.getDesc(), query, case_sensitive, partial ) ) {
+                            match = true;
+                            break;
+                        }
+                        if ( match( ann.getRef(), query, case_sensitive, partial ) ) {
+                            match = true;
+                            break;
+                        }
+                    }
+                }
+                if ( !match && node.getNodeData().isHasSequence()
+                        && ( node.getNodeData().getSequence().getCrossReferences() != null ) ) {
+                    for( final Accession x : node.getNodeData().getSequence().getCrossReferences() ) {
+                        if ( match( x.getComment(), query, case_sensitive, partial ) ) {
+                            match = true;
+                            break;
+                        }
+                        if ( match( x.getSource(), query, case_sensitive, partial ) ) {
+                            match = true;
+                            break;
+                        }
+                        if ( match( x.getValue(), query, case_sensitive, partial ) ) {
+                            match = true;
+                            break;
+                        }
+                    }
+                }
+                //
                 if ( !match && ( node.getNodeData().getBinaryCharacters() != null ) ) {
                     Iterator<String> it = node.getNodeData().getBinaryCharacters().getPresentCharacters().iterator();
                     I: while ( it.hasNext() ) {
@@ -1077,6 +1194,12 @@ public class PhylogenyMethods {
         return nodes;
     }
 
+    public static void setAllIndicatorsToZero( final Phylogeny phy ) {
+        for( final PhylogenyNodeIterator it = phy.iteratorPostorder(); it.hasNext(); ) {
+            it.next().setIndicator( ( byte ) 0 );
+        }
+    }
+
     /**
      * Convenience method.
      * Sets value for the first confidence value (created if not present, values overwritten otherwise). 
@@ -1180,6 +1303,11 @@ public class PhylogenyMethods {
                         return n1.getNodeData().getSequence().getSymbol()
                                 .compareTo( n2.getNodeData().getSequence().getSymbol() );
                     }
+                    if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getGeneName() ) )
+                            && ( !ForesterUtil.isEmpty( n2.getNodeData().getSequence().getGeneName() ) ) ) {
+                        return n1.getNodeData().getSequence().getGeneName()
+                                .compareTo( n2.getNodeData().getSequence().getGeneName() );
+                    }
                     if ( ( n1.getNodeData().getSequence().getAccession() != null )
                             && ( n2.getNodeData().getSequence().getAccession() != null )
                             && !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getAccession().getValue() )
@@ -1209,6 +1337,11 @@ public class PhylogenyMethods {
                         return n1.getNodeData().getSequence().getSymbol()
                                 .compareTo( n2.getNodeData().getSequence().getSymbol() );
                     }
+                    if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getGeneName() ) )
+                            && ( !ForesterUtil.isEmpty( n2.getNodeData().getSequence().getGeneName() ) ) ) {
+                        return n1.getNodeData().getSequence().getGeneName()
+                                .compareTo( n2.getNodeData().getSequence().getGeneName() );
+                    }
                     if ( ( n1.getNodeData().getSequence().getAccession() != null )
                             && ( n2.getNodeData().getSequence().getAccession() != null )
                             && !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getAccession().getValue() )
@@ -1275,6 +1408,11 @@ public class PhylogenyMethods {
                         return n1.getNodeData().getSequence().getSymbol()
                                 .compareTo( n2.getNodeData().getSequence().getSymbol() );
                     }
+                    if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getGeneName() ) )
+                            && ( !ForesterUtil.isEmpty( n2.getNodeData().getSequence().getGeneName() ) ) ) {
+                        return n1.getNodeData().getSequence().getGeneName()
+                                .compareTo( n2.getNodeData().getSequence().getGeneName() );
+                    }
                     if ( ( n1.getNodeData().getSequence().getAccession() != null )
                             && ( n2.getNodeData().getSequence().getAccession() != null )
                             && !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getAccession().getValue() )
@@ -1323,13 +1461,16 @@ public class PhylogenyMethods {
             if ( !n.getNodeData().isHasTaxonomy() ) {
                 throw new IllegalArgumentException( "no taxonomic data in node: " + n );
             }
-            //  ref_ext_taxo.add( getSpecies( n ) );
             if ( !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getScientificName() ) ) {
                 ref_ext_taxo.add( n.getNodeData().getTaxonomy().getScientificName() );
             }
             if ( !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getTaxonomyCode() ) ) {
                 ref_ext_taxo.add( n.getNodeData().getTaxonomy().getTaxonomyCode() );
             }
+            if ( ( n.getNodeData().getTaxonomy().getIdentifier() != null )
+                    && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getIdentifier().getValue() ) ) {
+                ref_ext_taxo.add( n.getNodeData().getTaxonomy().getIdentifier().getValuePlusProvider() );
+            }
         }
         final ArrayList<PhylogenyNode> nodes_to_delete = new ArrayList<PhylogenyNode>();
         for( final PhylogenyNodeIterator it = to_be_stripped.iteratorExternalForward(); it.hasNext(); ) {
@@ -1338,7 +1479,9 @@ public class PhylogenyMethods {
                 nodes_to_delete.add( n );
             }
             else if ( !( ref_ext_taxo.contains( n.getNodeData().getTaxonomy().getScientificName() ) )
-                    && !( ref_ext_taxo.contains( n.getNodeData().getTaxonomy().getTaxonomyCode() ) ) ) {
+                    && !( ref_ext_taxo.contains( n.getNodeData().getTaxonomy().getTaxonomyCode() ) )
+                    && !( ( n.getNodeData().getTaxonomy().getIdentifier() != null ) && ref_ext_taxo.contains( n
+                            .getNodeData().getTaxonomy().getIdentifier().getValuePlusProvider() ) ) ) {
                 nodes_to_delete.add( n );
             }
         }
@@ -1371,26 +1514,51 @@ public class PhylogenyMethods {
         }
     }
 
-    final static public void transferInternalNodeNamesToConfidence( final Phylogeny phy ) {
+    final static public boolean isInternalNamesLookLikeConfidences( final Phylogeny phy ) {
         final PhylogenyNodeIterator it = phy.iteratorPostorder();
         while ( it.hasNext() ) {
             final PhylogenyNode n = it.next();
-            if ( !n.isExternal() && !n.getBranchData().isHasConfidences() ) {
+            if ( !n.isExternal() && !n.isRoot() ) {
                 if ( !ForesterUtil.isEmpty( n.getName() ) ) {
-                    double d = -1.0;
+                    double value = -1;
                     try {
-                        d = Double.parseDouble( n.getName() );
+                        value = Double.parseDouble( n.getName() );
                     }
-                    catch ( final Exception e ) {
-                        d = -1.0;
+                    catch ( final NumberFormatException e ) {
+                        return false;
                     }
-                    if ( d >= 0.0 ) {
-                        n.getBranchData().addConfidence( new Confidence( d, "" ) );
-                        n.setName( "" );
+                    if ( ( value < 0.0 ) || ( value > 100 ) ) {
+                        return false;
                     }
                 }
             }
         }
+        return true;
+    }
+
+    final static public void transferInternalNodeNamesToConfidence( final Phylogeny phy, final String confidence_type ) {
+        final PhylogenyNodeIterator it = phy.iteratorPostorder();
+        while ( it.hasNext() ) {
+            transferInternalNodeNameToConfidence( confidence_type, it.next() );
+        }
+    }
+
+    private static void transferInternalNodeNameToConfidence( final String confidence_type, final PhylogenyNode n ) {
+        if ( !n.isExternal() && !n.getBranchData().isHasConfidences() ) {
+            if ( !ForesterUtil.isEmpty( n.getName() ) ) {
+                double d = -1.0;
+                try {
+                    d = Double.parseDouble( n.getName() );
+                }
+                catch ( final Exception e ) {
+                    d = -1.0;
+                }
+                if ( d >= 0.0 ) {
+                    n.getBranchData().addConfidence( new Confidence( d, confidence_type ) );
+                    n.setName( "" );
+                }
+            }
+        }
     }
 
     final static public void transferNodeNameToField( final Phylogeny phy,
@@ -1494,6 +1662,24 @@ public class PhylogenyMethods {
         return PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT;
     }
 
+    static double calculateDistanceToAncestor( final PhylogenyNode anc, PhylogenyNode desc ) {
+        double d = 0;
+        boolean all_default = true;
+        while ( anc != desc ) {
+            if ( desc.getDistanceToParent() != PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT ) {
+                d += desc.getDistanceToParent();
+                if ( all_default ) {
+                    all_default = false;
+                }
+            }
+            desc = desc.getParent();
+        }
+        if ( all_default ) {
+            return PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT;
+        }
+        return d;
+    }
+
     /**
      * Deep copies the phylogeny originating from this node.
      */
@@ -1530,6 +1716,20 @@ public class PhylogenyMethods {
         }
     }
 
+    private final static List<PhylogenyNode> divideIntoSubTreesHelper( final PhylogenyNode node,
+                                                                       final double min_distance_to_root ) {
+        final List<PhylogenyNode> l = new ArrayList<PhylogenyNode>();
+        final PhylogenyNode r = moveTowardsRoot( node, min_distance_to_root );
+        for( final PhylogenyNode ext : r.getAllExternalDescendants() ) {
+            if ( ext.getIndicator() != 0 ) {
+                throw new RuntimeException( "this should not have happened" );
+            }
+            ext.setIndicator( ( byte ) 1 );
+            l.add( ext );
+        }
+        return l;
+    }
+
     /**
      * Calculates the distance between PhylogenyNodes n1 and n2.
      * PRECONDITION: n1 is a descendant of n2.
@@ -1567,23 +1767,33 @@ public class PhylogenyMethods {
             return my_s.indexOf( my_query ) >= 0;
         }
         else {
-            return my_s.equals( my_query );
+            return Pattern.compile( "(\\b|_)" + Pattern.quote( my_query ) + "(\\b|_)" ).matcher( my_s ).find();
+        }
+    }
+
+    private final static PhylogenyNode moveTowardsRoot( final PhylogenyNode node, final double min_distance_to_root ) {
+        PhylogenyNode n = node;
+        PhylogenyNode prev = node;
+        while ( min_distance_to_root < n.calculateDistanceToRoot() ) {
+            prev = n;
+            n = n.getParent();
         }
+        return prev;
     }
 
     public static enum DESCENDANT_SORT_PRIORITY {
-        TAXONOMY, SEQUENCE, NODE_NAME;
+        NODE_NAME, SEQUENCE, TAXONOMY;
     }
 
     public static enum PhylogenyNodeField {
         CLADE_NAME,
+        SEQUENCE_NAME,
+        SEQUENCE_SYMBOL,
         TAXONOMY_CODE,
-        TAXONOMY_SCIENTIFIC_NAME,
         TAXONOMY_COMMON_NAME,
-        SEQUENCE_SYMBOL,
-        SEQUENCE_NAME,
+        TAXONOMY_ID,
         TAXONOMY_ID_UNIPROT_1,
         TAXONOMY_ID_UNIPROT_2,
-        TAXONOMY_ID;
+        TAXONOMY_SCIENTIFIC_NAME;
     }
 }