42470baf3c75c5b37651a5e3d3f868cdde6f8f36
[jalview.git] / forester / java / src / org / forester / phylogeny / Phylogeny.java
1 // $Id:
2 // FORESTER -- software libraries and applications
3 // for evolutionary biology research and applications.
4 //
5 // Copyright (C) 2008-2009 Christian M. Zmasek
6 // Copyright (C) 2008-2009 Burnham Institute for Medical Research
7 // Copyright (C) 2000-2001 Washington University School of Medicine
8 // and Howard Hughes Medical Institute
9 // All rights reserved
10 //
11 // This library is free software; you can redistribute it and/or
12 // modify it under the terms of the GNU Lesser General Public
13 // License as published by the Free Software Foundation; either
14 // version 2.1 of the License, or (at your option) any later version.
15 //
16 // This library is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 // Lesser General Public License for more details.
20 //
21 // You should have received a copy of the GNU Lesser General Public
22 // License along with this library; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 //
25 // Contact: phylosoft @ gmail . com
26 // WWW: www.phylosoft.org/forester
27
28 package org.forester.phylogeny;
29
30 import java.io.IOException;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.Collection;
34 import java.util.HashMap;
35 import java.util.Iterator;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.NoSuchElementException;
39 import java.util.Vector;
40
41 import org.forester.io.writers.PhylogenyWriter;
42 import org.forester.phylogeny.PhylogenyNodeI.NH_CONVERSION_SUPPORT_VALUE_STYLE;
43 import org.forester.phylogeny.data.BranchData;
44 import org.forester.phylogeny.data.Confidence;
45 import org.forester.phylogeny.data.Identifier;
46 import org.forester.phylogeny.data.PhylogenyDataUtil;
47 import org.forester.phylogeny.data.Sequence;
48 import org.forester.phylogeny.data.SequenceRelation;
49 import org.forester.phylogeny.data.SequenceRelation.SEQUENCE_RELATION_TYPE;
50 import org.forester.phylogeny.iterators.ExternalForwardIterator;
51 import org.forester.phylogeny.iterators.LevelOrderTreeIterator;
52 import org.forester.phylogeny.iterators.PhylogenyNodeIterator;
53 import org.forester.phylogeny.iterators.PostorderTreeIterator;
54 import org.forester.phylogeny.iterators.PreorderTreeIterator;
55 import org.forester.util.FailedConditionCheckException;
56 import org.forester.util.ForesterUtil;
57
58 public class Phylogeny {
59
60     public final static boolean                                 ALLOW_MULTIPLE_PARENTS_DEFAULT = false;
61     private PhylogenyNode                                       _root;
62     private boolean                                             _rooted;
63     private boolean                                             _allow_multiple_parents;
64     private String                                              _name;
65     private String                                              _type;
66     private String                                              _description;
67     private String                                              _distance_unit;
68     private Confidence                                          _confidence;
69     private Identifier                                          _identifier;
70     private boolean                                             _rerootable;
71     private HashMap<Integer, PhylogenyNode>                     _idhash;
72     private List<PhylogenyNode>                                 _external_nodes_set;
73     private Collection<Sequence>                                _sequenceRelationQueries;
74     private Collection<SequenceRelation.SEQUENCE_RELATION_TYPE> _relevant_sequence_relation_types;
75
76     /**
77      * Default Phylogeny constructor. Constructs an empty Phylogeny.
78      */
79     public Phylogeny() {
80         init();
81     }
82
83     /**
84      * Adds this Phylogeny to the list of child nodes of PhylogenyNode parent
85      * and sets the parent of this to parent.
86      * 
87      * @param n
88      *            the PhylogenyNode to add
89      */
90     public void addAsChild( final PhylogenyNode parent ) {
91         if ( isEmpty() ) {
92             throw new IllegalArgumentException( "Attempt to add an empty tree." );
93         }
94         if ( !isRooted() ) {
95             throw new IllegalArgumentException( "Attempt to add an unrooted tree." );
96         }
97         parent.addAsChild( getRoot() );
98         externalNodesHaveChanged();
99     }
100
101     public void addAsSibling( final PhylogenyNode sibling ) {
102         if ( isEmpty() ) {
103             throw new IllegalArgumentException( "Attempt to add an empty tree." );
104         }
105         if ( !isRooted() ) {
106             throw new IllegalArgumentException( "Attempt to add an unrooted tree." );
107         }
108         final int sibling_index = sibling.getChildNodeIndex();
109         final PhylogenyNode new_node = new PhylogenyNode();
110         final PhylogenyNode sibling_parent = sibling.getParent();
111         new_node.setChild1( sibling );
112         new_node.setChild2( getRoot() );
113         new_node.setParent( sibling_parent );
114         sibling.setParent( new_node );
115         sibling_parent.setChildNode( sibling_index, new_node );
116         final double new_dist = sibling.getDistanceToParent() == PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT ? PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT
117                 : sibling.getDistanceToParent() / 2;
118         new_node.setDistanceToParent( new_dist );
119         sibling.setDistanceToParent( new_dist );
120         externalNodesHaveChanged();
121     }
122
123     /**
124      * This calculates the height of the subtree emanating at n for rooted,
125      * tree-shaped phylogenies
126      * 
127      * @param n
128      *            the root-node of a subtree
129      * @return the height of the subtree emanating at n
130      */
131     public double calculateSubtreeHeight( final PhylogenyNode n ) {
132         if ( n.isExternal() || n.isCollapse() ) {
133             return ForesterUtil.isLargerOrEqualToZero( n.getDistanceToParent() );
134         }
135         else {
136             double max = -Double.MAX_VALUE;
137             for( int i = 0; i < n.getNumberOfDescendants(); ++i ) {
138                 final double l = calculateSubtreeHeight( n.getChildNode( i ) );
139                 if ( l > max ) {
140                     max = l;
141                 }
142             }
143             return max + ForesterUtil.isLargerOrEqualToZero( n.getDistanceToParent() );
144         }
145     }
146
147     /**
148      * Returns a deep copy of this Phylogeny.
149      * <p>
150      * (The resulting Phylogeny has its references in the external nodes
151      * corrected, if they are lacking/obsolete in this.)
152      */
153     public Phylogeny copy() {
154         return copy( _root );
155     }
156
157     /**
158      * Returns a shallow copy of this Phylogeny.
159      * <p>
160      * (The resulting Phylogeny has its references in the external nodes
161      * corrected, if they are lacking/obsolete in this.)
162      */
163     public Phylogeny copyShallow() {
164         return copyShallow( _root );
165     }
166
167     public Phylogeny copyShallow( final PhylogenyNode source ) {
168         final Phylogeny tree = new Phylogeny();
169         if ( isEmpty() ) {
170             tree.init();
171             return tree;
172         }
173         tree._rooted = _rooted;
174         tree._name = _name;
175         tree._description = _description;
176         tree._type = _type;
177         tree._rerootable = _rerootable;
178         tree._distance_unit = _distance_unit;
179         tree._confidence = _confidence;
180         tree._identifier = _identifier;
181         tree.setAllowMultipleParents( isAllowMultipleParents() );
182         tree._root = PhylogenyMethods.copySubTreeShallow( source );
183         return tree;
184     }
185
186     /**
187      * Returns a deep copy of this Phylogeny.
188      * <p>
189      * (The resulting Phylogeny has its references in the external nodes
190      * corrected, if they are lacking/obsolete in this.)
191      */
192     public Phylogeny copy( final PhylogenyNode source ) {
193         final Phylogeny tree = new Phylogeny();
194         if ( isEmpty() ) {
195             tree.init();
196             return tree;
197         }
198         tree._rooted = _rooted;
199         tree._name = new String( _name );
200         tree._description = new String( _description );
201         tree._type = new String( _type );
202         tree._rerootable = _rerootable;
203         tree._distance_unit = new String( _distance_unit );
204         if ( _confidence != null ) {
205             tree._confidence = ( Confidence ) _confidence.copy();
206         }
207         if ( _identifier != null ) {
208             tree._identifier = ( Identifier ) _identifier.copy();
209         }
210         tree.setAllowMultipleParents( isAllowMultipleParents() );
211         tree._root = PhylogenyMethods.copySubTree( source );
212         return tree;
213     }
214
215     /**
216      * Need the delete and/or rehash _idhash (not done automatically
217      * to allow client multiple deletions in linear time).
218      * Need to call 'recalculateNumberOfExternalDescendants(boolean)' after this 
219      * if tree is to be displayed.
220      * 
221      * @param remove_us the parent node of the subtree to be deleted
222      */
223     public void deleteSubtree( final PhylogenyNode remove_us, final boolean collapse_resulting_node_with_one_desc ) {
224         if ( isEmpty() || ( remove_us.isRoot() && getNumberOfExternalNodes() != 1 ) ) {
225             return;
226         }
227         if ( remove_us.isRoot() && getNumberOfExternalNodes() == 1 ) {
228             init();
229         }
230         else if ( !collapse_resulting_node_with_one_desc ) {
231             remove_us.getParent().removeChildNode( remove_us );
232         }
233         else {
234             final PhylogenyNode removed_node = remove_us;
235             final PhylogenyNode p = remove_us.getParent();
236             if ( p.isRoot() ) {
237                 if ( p.getNumberOfDescendants() == 2 ) {
238                     if ( removed_node.isFirstChildNode() ) {
239                         setRoot( getRoot().getChildNode( 1 ) );
240                         getRoot().setParent( null );
241                     }
242                     else {
243                         setRoot( getRoot().getChildNode( 0 ) );
244                         getRoot().setParent( null );
245                     }
246                 }
247                 else {
248                     p.removeChildNode( removed_node.getChildNodeIndex() );
249                 }
250             }
251             else {
252                 final PhylogenyNode pp = removed_node.getParent().getParent();
253                 if ( p.getNumberOfDescendants() == 2 ) {
254                     final int pi = p.getChildNodeIndex();
255                     if ( removed_node.isFirstChildNode() ) {
256                         p.getChildNode( 1 ).setDistanceToParent( PhylogenyMethods.addPhylogenyDistances( p
257                                 .getDistanceToParent(), p.getChildNode( 1 ).getDistanceToParent() ) );
258                         pp.setChildNode( pi, p.getChildNode( 1 ) );
259                     }
260                     else {
261                         p.getChildNode( 0 ).setDistanceToParent( PhylogenyMethods.addPhylogenyDistances( p
262                                 .getDistanceToParent(), p.getChildNode( 0 ).getDistanceToParent() ) );
263                         pp.setChildNode( pi, p.getChildNode( 0 ) );
264                     }
265                 }
266                 else {
267                     p.removeChildNode( removed_node.getChildNodeIndex() );
268                 }
269             }
270         }
271         remove_us.removeConnections();
272         setIdHash( null );
273         externalNodesHaveChanged();
274     }
275
276     public void externalNodesHaveChanged() {
277         _external_nodes_set = null;
278     }
279
280     public String[] getAllExternalNodeNames() {
281         int i = 0;
282         if ( isEmpty() ) {
283             return null;
284         }
285         final String[] names = new String[ getNumberOfExternalNodes() ];
286         for( final PhylogenyNodeIterator iter = iteratorExternalForward(); iter.hasNext(); ) {
287             names[ i++ ] = new String( iter.next().getName() );
288         }
289         return names;
290     }
291
292     public Confidence getConfidence() {
293         return _confidence;
294     }
295
296     public String getDescription() {
297         return _description;
298     }
299
300     public String getDistanceUnit() {
301         return _distance_unit;
302     }
303
304     /**
305      * 
306      * Warning. The order of the returned nodes is random
307      * -- and hence cannot be relied on.
308      * 
309      * @return Unordered set of PhylogenyNode
310      */
311     public List<PhylogenyNode> getExternalNodes() {
312         if ( _external_nodes_set == null ) {
313             _external_nodes_set = new ArrayList<PhylogenyNode>();
314             for( final PhylogenyNodeIterator it = iteratorPostorder(); it.hasNext(); ) {
315                 final PhylogenyNode n = it.next();
316                 if ( n.isExternal() ) {
317                     _external_nodes_set.add( n );
318                 }
319             }
320         }
321         return _external_nodes_set;
322     }
323
324     /**
325      * Returns the number of duplications of this Phylogeny (int). A return
326      * value of -1 indicates that the number of duplications is unknown.
327      */
328     // public int getNumberOfDuplications() {
329     // return _number_of_duplications;
330     // } // getNumberOfDuplications()
331     /**
332      * Sets the number of duplications of this Phylogeny (int). A value of -1
333      * indicates that the number of duplications is unknown.
334      * 
335      * @param clean_nh
336      *            set to true for clean NH format
337      */
338     // public void setNumberOfDuplications( int i ) {
339     // if ( i < 0 ) {
340     // _number_of_duplications = -1;
341     // }
342     // else {
343     // _number_of_duplications = i;
344     // }
345     // } // setNumberOfDuplications( int )
346     /**
347      * Returns the first external PhylogenyNode.
348      */
349     public PhylogenyNode getFirstExternalNode() {
350         if ( isEmpty() ) {
351             throw new FailedConditionCheckException( "attempt to obtain first external node of empty phylogeney" );
352         }
353         PhylogenyNode node = getRoot();
354         while ( node.isInternal() ) {
355             node = node.getFirstChildNode();
356         }
357         return node;
358     }
359
360     /**
361      * This calculates the height for rooted, tree-shaped phylogenies. The
362      * height is the longest distance from the root to an external node. Please
363      * note. Child nodes of collapsed nodes are ignored -- which is useful for
364      * display purposes but might be misleading for other applications.
365      * 
366      * @return the height for rooted, tree-shaped phylogenies
367      */
368     public double getHeight() {
369         if ( isEmpty() ) {
370             return 0.0;
371         }
372         return calculateSubtreeHeight( getRoot() );
373     }
374
375     public Identifier getIdentifier() {
376         return _identifier;
377     }
378
379     // ---------------------------------------------------------
380     // Modification of Phylogeny topology and Phylogeny appearance
381     // ---------------------------------------------------------
382     private HashMap<Integer, PhylogenyNode> getIdHash() {
383         return _idhash;
384     }
385
386     /**
387      * Returns the name of this Phylogeny.
388      */
389     public String getName() {
390         return _name;
391     }
392
393     /**
394      * Finds the PhylogenyNode of this Phylogeny which has a matching ID number.
395      * Takes O(n) time. After method hashIDs() has been called it runs in
396      * constant time.
397      * 
398      * @param id
399      *            ID number (int) of the PhylogenyNode to find
400      * @return PhylogenyNode with matching ID, null if not found
401      */
402     public PhylogenyNode getNode( final int id ) throws NoSuchElementException {
403         if ( isEmpty() ) {
404             throw new NoSuchElementException( "attempt to get node in an empty phylogeny" );
405         }
406         if ( _idhash != null ) {
407             return _idhash.get( id );
408         }
409         else {
410             for( final PhylogenyNodeIterator iter = iteratorPreorder(); iter.hasNext(); ) {
411                 final PhylogenyNode node = iter.next();
412                 if ( node.getId() == id ) {
413                     return node;
414                 }
415             }
416         }
417         return null;
418     }
419
420     /**
421      * Returns a PhylogenyNode of this Phylogeny which has a matching name.
422      * Throws an Exception if seqname is not present in this or not unique.
423      * 
424      * @param name
425      *            name (String) of PhylogenyNode to find
426      * @return PhylogenyNode with matchin name
427      */
428     public PhylogenyNode getNode( final String name ) {
429         if ( isEmpty() ) {
430             return null;
431         }
432         final List<PhylogenyNode> nodes = getNodes( name );
433         if ( ( nodes == null ) || ( nodes.size() < 1 ) ) {
434             throw new IllegalArgumentException( "node named [" + name + "] not found" );
435         }
436         if ( nodes.size() > 1 ) {
437             throw new IllegalArgumentException( "node named [" + name + "] not unique" );
438         }
439         return nodes.get( 0 );
440     }
441
442     /**
443      * Return Node by TaxonomyId Olivier CHABROL :
444      * olivier.chabrol@univ-provence.fr
445      * 
446      * @param taxonomyID
447      *            search taxonomy identifier
448      * @param nodes
449      *            sublist node to search
450      * @return List node with the same taxonomy identifier
451      */
452     private List<PhylogenyNode> getNodeByTaxonomyID( final String taxonomyID, final List<PhylogenyNode> nodes ) {
453         final List<PhylogenyNode> retour = new ArrayList<PhylogenyNode>();
454         for( final PhylogenyNode node : nodes ) {
455             if ( taxonomyID.equals( PhylogenyMethods.getTaxonomyIdentifier( node ) ) ) {
456                 retour.add( node );
457             }
458         }
459         return retour;
460     }
461
462     /**
463      * Returns a List with references to all Nodes of this Phylogeny which have
464      * a matching name.
465      * 
466      * @param name
467      *            name (String) of Nodes to find
468      * @return Vector of references to Nodes of this Phylogeny with matching
469      *         names
470      * @see #getNodesWithMatchingSpecies(String)
471      */
472     public List<PhylogenyNode> getNodes( final String name ) {
473         if ( isEmpty() ) {
474             return null;
475         }
476         final List<PhylogenyNode> nodes = new ArrayList<PhylogenyNode>();
477         for( final PhylogenyNodeIterator iter = iteratorPreorder(); iter.hasNext(); ) {
478             final PhylogenyNode n = iter.next();
479             if ( n.getName().equals( name ) ) {
480                 nodes.add( n );
481             }
482         }
483         return nodes;
484     }
485
486     public List<PhylogenyNode> getNodesViaSequenceName( final String seq_name ) {
487         if ( isEmpty() ) {
488             return null;
489         }
490         final List<PhylogenyNode> nodes = new ArrayList<PhylogenyNode>();
491         for( final PhylogenyNodeIterator iter = iteratorPreorder(); iter.hasNext(); ) {
492             final PhylogenyNode n = iter.next();
493             if ( n.getNodeData().isHasSequence() && n.getNodeData().getSequence().getName().equals( seq_name ) ) {
494                 nodes.add( n );
495             }
496         }
497         return nodes;
498     }
499
500     public List<PhylogenyNode> getNodesViaTaxonomyCode( final String taxonomy_code ) {
501         if ( isEmpty() ) {
502             return null;
503         }
504         final List<PhylogenyNode> nodes = new ArrayList<PhylogenyNode>();
505         for( final PhylogenyNodeIterator iter = iteratorPreorder(); iter.hasNext(); ) {
506             final PhylogenyNode n = iter.next();
507             if ( n.getNodeData().isHasTaxonomy()
508                     && n.getNodeData().getTaxonomy().getTaxonomyCode().equals( taxonomy_code ) ) {
509                 nodes.add( n );
510             }
511         }
512         return nodes;
513     }
514
515     /**
516      * Returns a Vector with references to all Nodes of this Phylogeny which
517      * have a matching species name.
518      * 
519      * @param specname
520      *            species name (String) of Nodes to find
521      * @return Vector of references to Nodes of this Phylogeny with matching
522      *         species names.
523      * @see #getNodes(String)
524      */
525     public List<PhylogenyNode> getNodesWithMatchingSpecies( final String specname ) {
526         if ( isEmpty() ) {
527             return null;
528         }
529         final List<PhylogenyNode> nodes = new ArrayList<PhylogenyNode>();
530         for( final PhylogenyNodeIterator iter = iteratorPreorder(); iter.hasNext(); ) {
531             final PhylogenyNode n = iter.next();
532             if ( PhylogenyMethods.getSpecies( n ).equals( specname ) ) {
533                 nodes.add( n );
534             }
535         }
536         return nodes;
537     }
538
539     public PhylogenyNode getNodeViaSequenceName( final String seq_name ) {
540         if ( isEmpty() ) {
541             return null;
542         }
543         final List<PhylogenyNode> nodes = getNodesViaSequenceName( seq_name );
544         if ( ( nodes == null ) || ( nodes.size() < 1 ) ) {
545             throw new IllegalArgumentException( "node with sequence named [" + seq_name + "] not found" );
546         }
547         if ( nodes.size() > 1 ) {
548             throw new IllegalArgumentException( "node with sequence named [" + seq_name + "] not unique" );
549         }
550         return nodes.get( 0 );
551     }
552
553     public PhylogenyNode getNodeViaTaxonomyCode( final String taxonomy_code ) {
554         if ( isEmpty() ) {
555             return null;
556         }
557         final List<PhylogenyNode> nodes = getNodesViaTaxonomyCode( taxonomy_code );
558         if ( ( nodes == null ) || ( nodes.size() < 1 ) ) {
559             throw new IllegalArgumentException( "node with taxonomy code \"" + taxonomy_code + "\" not found" );
560         }
561         if ( nodes.size() > 1 ) {
562             throw new IllegalArgumentException( "node with taxonomy code \"" + taxonomy_code + "\" not unique" );
563         }
564         return nodes.get( 0 );
565     }
566
567     /**
568      * This is time-inefficient since it runs a iterator each time it is called.
569      * 
570      */
571     public int getNodeCount() {
572         if ( isEmpty() ) {
573             return 0;
574         }
575         int c = 0;
576         for( final PhylogenyNodeIterator it = iteratorPreorder(); it.hasNext(); it.next() ) {
577             ++c;
578         }
579         return c;
580     }
581
582     public int getNumberOfBranches() {
583         if ( isEmpty() ) {
584             return 0;
585         }
586         int c = 0;
587         for( final PhylogenyNodeIterator iter = iteratorPreorder(); iter.hasNext(); iter.next() ) {
588             ++c;
589         }
590         if ( !isRooted() ) {
591             --c;
592         }
593         return c;
594     }
595
596     /**
597      * Returns the sum of external Nodes of this Phylogeny (int).
598      */
599     public int getNumberOfExternalNodes() {
600         if ( isEmpty() ) {
601             return 0;
602         }
603         return getExternalNodes().size();
604     }
605
606     /**
607      * Returns all paralogs of the external PhylogenyNode n of this Phylogeny.
608      * paralog are returned as List of node references.
609      * <p>
610      * PRECONDITION: This tree must be binary and rooted, and speciation -
611      * duplication need to be assigned for each of its internal Nodes.
612      * <p>
613      * Returns null if this Phylogeny is empty or if n is internal.
614      * <p>
615      * (Last modified: 11/22/00) Olivier CHABROL :
616      * olivier.chabrol@univ-provence.fr
617      * 
618      * @param n
619      *            external PhylogenyNode whose orthologs are to be returned
620      * @return Vector of references to all orthologous Nodes of PhylogenyNode n
621      *         of this Phylogeny, null if this Phylogeny is empty or if n is
622      *         internal
623      */
624     public List<PhylogenyNode> getParalogousNodes( final PhylogenyNode n, final String[] taxonomyCodeRange ) {
625         PhylogenyNode node = n;
626         PhylogenyNode prev = null;
627         final List<PhylogenyNode> v = new ArrayList<PhylogenyNode>();
628         final Map<PhylogenyNode, List<String>> map = new HashMap<PhylogenyNode, List<String>>();
629         getTaxonomyMap( getRoot(), map );
630         if ( !node.isExternal() || isEmpty() ) {
631             return null;
632         }
633         final String searchNodeSpeciesId = PhylogenyMethods.getTaxonomyIdentifier( n );
634         if ( !node.isExternal() || isEmpty() ) {
635             return null;
636         }
637         List<String> taxIdList = null;
638         final List<String> taxonomyCodeRangeList = Arrays.asList( taxonomyCodeRange );
639         while ( !node.isRoot() ) {
640             prev = node;
641             node = node.getParent();
642             taxIdList = map.get( node );
643             if ( node.isDuplication() && isContains( taxIdList, taxonomyCodeRangeList ) ) {
644                 if ( node.getChildNode1() == prev ) {
645                     v.addAll( getNodeByTaxonomyID( searchNodeSpeciesId, node.getChildNode2()
646                             .getAllExternalDescendants() ) );
647                 }
648                 else {
649                     v.addAll( getNodeByTaxonomyID( searchNodeSpeciesId, node.getChildNode1()
650                             .getAllExternalDescendants() ) );
651                 }
652             }
653         }
654         return v;
655     }
656
657     public Collection<SequenceRelation.SEQUENCE_RELATION_TYPE> getRelevantSequenceRelationTypes() {
658         if ( _relevant_sequence_relation_types == null ) {
659             _relevant_sequence_relation_types = new Vector<SEQUENCE_RELATION_TYPE>();
660         }
661         return _relevant_sequence_relation_types;
662     }
663
664     /**
665      * Returns the root PhylogenyNode of this Phylogeny.
666      */
667     public PhylogenyNode getRoot() {
668         return _root;
669     }
670
671     public Collection<Sequence> getSequenceRelationQueries() {
672         return _sequenceRelationQueries;
673     }
674
675     /**
676      * List all species contains in all leaf under a node Olivier CHABROL :
677      * olivier.chabrol@univ-provence.fr
678      * 
679      * @param node
680      *            PhylogenyNode whose sub node species are returned
681      * @return species contains in all leaf under the param node
682      */
683     private List<String> getSubNodeTaxonomy( final PhylogenyNode node ) {
684         final List<String> taxonomyList = new ArrayList<String>();
685         final List<PhylogenyNode> childs = node.getAllExternalDescendants();
686         String speciesId = null;
687         for( final PhylogenyNode phylogenyNode : childs ) {
688             // taxId = new Long(phylogenyNode.getTaxonomyID());
689             speciesId = PhylogenyMethods.getTaxonomyIdentifier( phylogenyNode );
690             if ( !taxonomyList.contains( speciesId ) ) {
691                 taxonomyList.add( speciesId );
692             }
693         }
694         return taxonomyList;
695     }
696
697     /**
698      * Create a map [<PhylogenyNode, List<String>], the list contains the
699      * species contains in all leaf under phylogeny node Olivier CHABROL :
700      * olivier.chabrol@univ-provence.fr
701      * 
702      * @param node
703      *            the tree root node
704      * @param map
705      *            map to fill
706      */
707     private void getTaxonomyMap( final PhylogenyNode node, final Map<PhylogenyNode, List<String>> map ) {
708         // node is leaf
709         if ( node.isExternal() ) {
710             return;
711         }
712         map.put( node, getSubNodeTaxonomy( node ) );
713         getTaxonomyMap( node.getChildNode1(), map );
714         getTaxonomyMap( node.getChildNode2(), map );
715     }
716
717     public String getType() {
718         return _type;
719     }
720
721     /**
722      * Hashes the ID number of each PhylogenyNode of this Phylogeny to its
723      * corresponding PhylogenyNode, in order to make method getNode( id ) run in
724      * constant time. Important: The user is responsible for calling this method
725      * (again) after this Phylogeny has been changed/created/renumbered.
726      */
727     public void hashIDs() {
728         if ( isEmpty() ) {
729             return;
730         }
731         setIdHash( new HashMap<Integer, PhylogenyNode>() );
732         for( final PhylogenyNodeIterator iter = iteratorPreorder(); iter.hasNext(); ) {
733             final PhylogenyNode node = iter.next();
734             getIdHash().put( node.getId(), node );
735         }
736     }
737
738     /**
739      * Deletes this Phylogeny.
740      */
741     public void init() {
742         _root = null;
743         _rooted = false;
744         _name = "";
745         _description = "";
746         _type = "";
747         _distance_unit = "";
748         _idhash = null;
749         _confidence = null;
750         _identifier = null;
751         _rerootable = true;
752         setAllowMultipleParents( Phylogeny.ALLOW_MULTIPLE_PARENTS_DEFAULT );
753     }
754
755     private boolean isAllowMultipleParents() {
756         return _allow_multiple_parents;
757     }
758
759     /**
760      * Returns whether this is a completely binary tree (i.e. all internal nodes
761      * are bifurcations).
762      * 
763      */
764     public boolean isCompletelyBinary() {
765         if ( isEmpty() ) {
766             return false;
767         }
768         for( final PhylogenyNodeIterator iter = iteratorPreorder(); iter.hasNext(); ) {
769             final PhylogenyNode node = iter.next();
770             if ( node.isInternal() && ( node.getNumberOfDescendants() != 2 ) ) {
771                 return false;
772             }
773         }
774         return true;
775     }
776
777     /**
778      * Util method to check if all element of a list is contains in the
779      * rangeList. Olivier CHABROL : olivier.chabrol@univ-provence.fr
780      * 
781      * @param list
782      *            list to be check
783      * @param rangeList
784      *            the range list to compare
785      * @return <code>true</code> if all param list element are contains in param
786      *         rangeList, <code>false</code> otherwise.
787      */
788     private boolean isContains( final List<String> list, final List<String> rangeList ) {
789         if ( list.size() > rangeList.size() ) {
790             return false;
791         }
792         String l = null;
793         for( final Iterator<String> iterator = list.iterator(); iterator.hasNext(); ) {
794             l = iterator.next();
795             if ( !rangeList.contains( l ) ) {
796                 return false;
797             }
798         }
799         return true;
800     }
801
802     /**
803      * Checks whether a Phylogeny object is deleted (or empty).
804      * 
805      * @return true if the tree is deleted (or empty), false otherwise
806      */
807     public boolean isEmpty() {
808         return ( getRoot() == null );
809     }
810
811     public boolean isRerootable() {
812         return _rerootable;
813     }
814
815     /**
816      * Returns true is this Phylogeny is rooted.
817      */
818     public boolean isRooted() {
819         return _rooted;
820     } // isRooted()
821
822     public boolean isTree() {
823         return true;
824     }
825
826     public PhylogenyNodeIterator iteratorExternalForward() {
827         return new ExternalForwardIterator( this );
828     }
829
830     public PhylogenyNodeIterator iteratorLevelOrder() {
831         return new LevelOrderTreeIterator( this );
832     }
833
834     public PhylogenyNodeIterator iteratorPostorder() {
835         return new PostorderTreeIterator( this );
836     }
837
838     public PhylogenyNodeIterator iteratorPreorder() {
839         return new PreorderTreeIterator( this );
840     }
841
842     /**
843      * Resets the ID numbers of the nodes of this Phylogeny in level order,
844      * starting with start_label (for the root). <br>
845      * WARNING. After this method has been called, node IDs are no longer
846      * unique. 
847      */
848     public void levelOrderReID() {
849         if ( isEmpty() ) {
850             return;
851         }
852         _idhash = null;
853         int max = 0;
854         for( final PhylogenyNodeIterator it = iteratorPreorder(); it.hasNext(); ) {
855             final PhylogenyNode node = it.next();
856             if ( node.isRoot() ) {
857                 node.setId( PhylogenyNode.getNodeCount() );
858             }
859             else {
860                 node.setId( node.getParent().getId() + 1 );
861                 if ( node.getId() > max ) {
862                     max = node.getId();
863                 }
864             }
865         }
866         PhylogenyNode.setNodeCount( max + 1 );
867     }
868
869     public void preOrderReId() {
870         if ( isEmpty() ) {
871             return;
872         }
873         setIdHash( null );
874         int i = PhylogenyNode.getNodeCount();
875         for( final PhylogenyNodeIterator it = iteratorPreorder(); it.hasNext(); ) {
876             it.next().setId( i++ );
877         }
878         PhylogenyNode.setNodeCount( i );
879     }
880
881     /**
882      * Prints descriptions of all external Nodes of this Phylogeny to
883      * System.out.
884      */
885     public void printExtNodes() {
886         if ( isEmpty() ) {
887             return;
888         }
889         for( final PhylogenyNodeIterator iter = iteratorExternalForward(); iter.hasNext(); ) {
890             System.out.println( iter.next() + "\n" );
891         }
892     }
893
894     /**
895      * (Re)counts the number of children for each PhylogenyNode of this
896      * Phylogeny. As an example, this method needs to be called after a
897      * Phylogeny has been reRooted and it is to be displayed.
898      * 
899      * @param consider_collapsed_nodes
900      *            set to true to take into account collapsed nodes (collapsed
901      *            nodes have 1 child).
902      */
903     public void recalculateNumberOfExternalDescendants( final boolean consider_collapsed_nodes ) {
904         if ( isEmpty() ) {
905             return;
906         }
907         for( final PhylogenyNodeIterator iter = iteratorPostorder(); iter.hasNext(); ) {
908             final PhylogenyNode node = iter.next();
909             if ( node.isExternal() || ( consider_collapsed_nodes && node.isCollapse() ) ) {
910                 node.setSumExtNodes( 1 );
911             }
912             else {
913                 int sum = 0;
914                 for( int i = 0; i < node.getNumberOfDescendants(); ++i ) {
915                     sum += node.getChildNode( i ).getNumberOfExternalNodes();
916                 }
917                 node.setSumExtNodes( sum );
918             }
919         }
920     }
921
922     /**
923      * Places the root of this Phylogeny on the parent branch of the
924      * PhylogenyNode with a corresponding ID. The new root is always placed on
925      * the middle of the branch. If the resulting reRooted Phylogeny is to be
926      * used any further, in most cases the following methods have to be called
927      * on the resulting Phylogeny:
928      * <p>
929      * <li>recalculateNumberOfExternalDescendants(boolean)
930      * <li>recalculateAndReset()
931      * 
932      * @param id
933      *            ID (int) of PhylogenyNode of this Phylogeny
934      */
935     public void reRoot( final int id ) {
936         reRoot( getNode( id ) );
937     }
938
939     /**
940      * Places the root of this Phylogeny on Branch b. The new root is always
941      * placed on the middle of the branch b.
942      * 
943      */
944     public void reRoot( final PhylogenyBranch b ) {
945         final PhylogenyNode n1 = b.getFirstNode();
946         final PhylogenyNode n2 = b.getSecondNode();
947         if ( n1.isExternal() ) {
948             reRoot( n1 );
949         }
950         else if ( n2.isExternal() ) {
951             reRoot( n2 );
952         }
953         else if ( ( n2 == n1.getChildNode1() ) || ( n2 == n1.getChildNode2() ) ) {
954             reRoot( n2 );
955         }
956         else if ( ( n1 == n2.getChildNode1() ) || ( n1 == n2.getChildNode2() ) ) {
957             reRoot( n1 );
958         }
959         else if ( ( n1.getParent() != null ) && n1.getParent().isRoot()
960                 && ( ( n1.getParent().getChildNode1() == n2 ) || ( n1.getParent().getChildNode2() == n2 ) ) ) {
961             reRoot( n1 );
962         }
963         else {
964             throw new IllegalArgumentException( "reRoot( Branch b ): b is not a branch." );
965         }
966     }
967
968     /**
969      * Places the root of this Phylogeny on the parent branch PhylogenyNode n.
970      * The new root is always placed on the middle of the branch.
971      * <p>
972      * If the resulting reRooted Phylogeny is to be used any further, in most
973      * cases the following three methods have to be called on the resulting
974      * Phylogeny:
975      * <ul>
976      * <li>recalculateNumberOfExternalDescendants(boolean) <li>recalculateAndReset()
977      * </ul>
978      * <p>
979      * (Last modified: 10/01/01)
980      * 
981      * @param n
982      *            PhylogenyNode of this Phylogeny\
983      */
984     public void reRoot( final PhylogenyNode n ) {
985         reRoot( n, -1 );
986     }
987
988     public void reRoot( final PhylogenyNode n, final double distance_n_to_parent ) {
989         if ( isEmpty() || ( getNumberOfExternalNodes() < 2 ) ) {
990             return;
991         }
992         setRooted( true );
993         if ( n.isRoot() ) {
994             return;
995         }
996         else if ( n.getParent().isRoot() ) {
997             if ( ( n.getParent().getNumberOfDescendants() == 2 ) && ( distance_n_to_parent >= 0 ) ) {
998                 final double d = n.getParent().getChildNode1().getDistanceToParent()
999                         + n.getParent().getChildNode2().getDistanceToParent();
1000                 PhylogenyNode other;
1001                 if ( n.getChildNodeIndex() == 0 ) {
1002                     other = n.getParent().getChildNode2();
1003                 }
1004                 else {
1005                     other = n.getParent().getChildNode1();
1006                 }
1007                 n.setDistanceToParent( distance_n_to_parent );
1008                 final double dm = d - distance_n_to_parent;
1009                 if ( dm >= 0 ) {
1010                     other.setDistanceToParent( dm );
1011                 }
1012                 else {
1013                     other.setDistanceToParent( 0 );
1014                 }
1015             }
1016             if ( n.getParent().getNumberOfDescendants() > 2 ) {
1017                 final int index = n.getChildNodeIndex();
1018                 final double dn = n.getDistanceToParent();
1019                 final PhylogenyNode prev_root = getRoot();
1020                 prev_root.getDescendants().remove( index );
1021                 final PhylogenyNode new_root = new PhylogenyNode();
1022                 new_root.setChildNode( 0, n );
1023                 new_root.setChildNode( 1, prev_root );
1024                 if ( n.getBranchDataDirectly() != null ) {
1025                     prev_root.setBranchData( ( BranchData ) n.getBranchDataDirectly().copy() );
1026                 }
1027                 setRoot( new_root );
1028                 if ( distance_n_to_parent >= 0 ) {
1029                     n.setDistanceToParent( distance_n_to_parent );
1030                     final double d = dn - distance_n_to_parent;
1031                     if ( d >= 0 ) {
1032                         prev_root.setDistanceToParent( d );
1033                     }
1034                     else {
1035                         prev_root.setDistanceToParent( 0 );
1036                     }
1037                 }
1038                 else {
1039                     if ( dn >= 0 ) {
1040                         final double d = dn / 2.0;
1041                         n.setDistanceToParent( d );
1042                         prev_root.setDistanceToParent( d );
1043                     }
1044                 }
1045             }
1046         }
1047         else {
1048             PhylogenyNode a = n;
1049             PhylogenyNode b = null;
1050             PhylogenyNode c = null;
1051             final PhylogenyNode new_root = new PhylogenyNode();
1052             double distance1 = 0.0;
1053             double distance2 = 0.0;
1054             BranchData branch_data_1 = null;
1055             BranchData branch_data_2 = null;
1056             b = a.getParent();
1057             c = b.getParent();
1058             new_root.setChildNode( 0, a );
1059             new_root.setChildNode( 1, b );
1060             distance1 = c.getDistanceToParent();
1061             if ( c.getBranchDataDirectly() != null ) {
1062                 branch_data_1 = ( BranchData ) c.getBranchDataDirectly().copy();
1063             }
1064             c.setDistanceToParent( b.getDistanceToParent() );
1065             if ( b.getBranchDataDirectly() != null ) {
1066                 c.setBranchData( ( BranchData ) b.getBranchDataDirectly().copy() );
1067             }
1068             if ( a.getBranchDataDirectly() != null ) {
1069                 b.setBranchData( ( BranchData ) a.getBranchDataDirectly().copy() );
1070             }
1071             // New root is always placed in the middle of the branch:
1072             if ( a.getDistanceToParent() == PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT ) {
1073                 b.setDistanceToParent( PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT );
1074             }
1075             else {
1076                 if ( distance_n_to_parent >= 0.0 ) {
1077                     final double diff = a.getDistanceToParent() - distance_n_to_parent;
1078                     a.setDistanceToParent( distance_n_to_parent );
1079                     b.setDistanceToParent( diff >= 0.0 ? diff : 0.0 );
1080                 }
1081                 else {
1082                     final double d = a.getDistanceToParent() / 2.0;
1083                     a.setDistanceToParent( d );
1084                     b.setDistanceToParent( d );
1085                 }
1086             }
1087             b.setChildNodeOnly( a.getChildNodeIndex( b ), c );
1088             // moving to the old root, swapping references:
1089             while ( !c.isRoot() ) {
1090                 a = b;
1091                 b = c;
1092                 c = c.getParent();
1093                 b.setChildNodeOnly( a.getChildNodeIndex( b ), c );
1094                 b.setParent( a );
1095                 distance2 = c.getDistanceToParent();
1096                 branch_data_2 = c.getBranchDataDirectly();
1097                 c.setDistanceToParent( distance1 );
1098                 c.setBranchData( branch_data_1 );
1099                 distance1 = distance2;
1100                 branch_data_1 = branch_data_2;
1101             }
1102             // removing the old root:
1103             if ( c.getNumberOfDescendants() == 2 ) {
1104                 final PhylogenyNode node = c.getChildNode( 1 - b.getChildNodeIndex( c ) );
1105                 node.setParent( b );
1106                 if ( ( c.getDistanceToParent() == PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT )
1107                         && ( node.getDistanceToParent() == PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT ) ) {
1108                     node.setDistanceToParent( PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT );
1109                 }
1110                 else {
1111                     node.setDistanceToParent( ( c.getDistanceToParent() >= 0.0 ? c.getDistanceToParent() : 0.0 )
1112                             + ( node.getDistanceToParent() >= 0.0 ? node.getDistanceToParent() : 0.0 ) );
1113                 }
1114                 if ( c.getBranchDataDirectly() != null ) {
1115                     node.setBranchData( ( BranchData ) c.getBranchDataDirectly().copy() );
1116                 }
1117                 for( int i = 0; i < b.getNumberOfDescendants(); ++i ) {
1118                     if ( b.getChildNode( i ) == c ) {
1119                         b.setChildNodeOnly( i, node );
1120                         break;
1121                     }
1122                 }
1123             }
1124             else {
1125                 c.setParent( b );
1126                 c.removeChildNode( b.getChildNodeIndex( c ) );
1127             }
1128             setRoot( new_root );
1129         }
1130     }
1131
1132     /**
1133      * Sets all Nodes of this Phylogeny to not-collapsed.
1134      * <p>
1135      * In most cases methods adjustNodeCount(false) and recalculateAndReset()
1136      * need to be called after this method has been called.
1137      */
1138     public void setAllNodesToNotCollapse() {
1139         if ( isEmpty() ) {
1140             return;
1141         }
1142         for( final PhylogenyNodeIterator iter = iteratorPreorder(); iter.hasNext(); ) {
1143             final PhylogenyNode node = iter.next();
1144             node.setCollapse( false );
1145         }
1146     }
1147
1148     private void setAllowMultipleParents( final boolean allow_multiple_parents ) {
1149         _allow_multiple_parents = allow_multiple_parents;
1150     }
1151
1152     public void setConfidence( final Confidence confidence ) {
1153         _confidence = confidence;
1154     }
1155
1156     public void setDescription( final String description ) {
1157         _description = description;
1158     }
1159
1160     public void setDistanceUnit( final String _distance_unit ) {
1161         this._distance_unit = _distance_unit;
1162     }
1163
1164     public void setIdentifier( final Identifier identifier ) {
1165         _identifier = identifier;
1166     }
1167
1168     void setIdHash( final HashMap<Integer, PhylogenyNode> idhash ) {
1169         _idhash = idhash;
1170     }
1171
1172     /**
1173      * Sets the indicators of all Nodes of this Phylogeny to 0.
1174      */
1175     public void setIndicatorsToZero() {
1176         if ( isEmpty() ) {
1177             return;
1178         }
1179         for( final PhylogenyNodeIterator iter = iteratorPreorder(); iter.hasNext(); ) {
1180             iter.next().setIndicator( ( byte ) 0 );
1181         }
1182     } // setIndicatorsToZero()
1183
1184     /**
1185      * Sets the name of this Phylogeny to s.
1186      */
1187     public void setName( final String s ) {
1188         _name = s;
1189     }
1190
1191     public void setRelevantSequenceRelationTypes( final Collection<SequenceRelation.SEQUENCE_RELATION_TYPE> types ) {
1192         _relevant_sequence_relation_types = types;
1193     }
1194
1195     public void setRerootable( final boolean rerootable ) {
1196         _rerootable = rerootable;
1197     }
1198
1199     public void setRoot( final PhylogenyNode n ) {
1200         _root = n;
1201     } // setRoot( PhylogenyNode )
1202
1203     /**
1204      * Sets whether this Phylogeny is rooted or not.
1205      */
1206     public void setRooted( final boolean b ) {
1207         _rooted = b;
1208     } // setRooted( boolean )
1209
1210     public void setSequenceRelationQueries( final Collection<Sequence> sequencesByName ) {
1211         _sequenceRelationQueries = sequencesByName;
1212     }
1213
1214     public void setType( final String type ) {
1215         _type = type;
1216     }
1217
1218     public String toNewHampshire() {
1219         return toNewHampshire( false, NH_CONVERSION_SUPPORT_VALUE_STYLE.NONE );
1220     }
1221
1222     public String toNewHampshire( final boolean simple_nh,
1223                                   final NH_CONVERSION_SUPPORT_VALUE_STYLE nh_conversion_support_style ) {
1224         try {
1225             return new PhylogenyWriter().toNewHampshire( this, simple_nh, true, nh_conversion_support_style )
1226                     .toString();
1227         }
1228         catch ( final IOException e ) {
1229             throw new Error( "this should not have happend: " + e.getMessage() );
1230         }
1231     }
1232
1233     public String toNewHampshireX() {
1234         try {
1235             return new PhylogenyWriter().toNewHampshireX( this ).toString();
1236         }
1237         catch ( final IOException e ) {
1238             throw new Error( "this should not have happend: " + e.getMessage() );
1239         }
1240     }
1241
1242     public String toNexus( final NH_CONVERSION_SUPPORT_VALUE_STYLE svs ) {
1243         try {
1244             return new PhylogenyWriter().toNexus( this, svs ).toString();
1245         }
1246         catch ( final IOException e ) {
1247             throw new Error( "this should not have happend: " + e.getMessage() );
1248         }
1249     }
1250
1251     public String toNexus() {
1252         return toNexus( NH_CONVERSION_SUPPORT_VALUE_STYLE.NONE );
1253     }
1254
1255     public String toPhyloXML( final int phyloxml_level ) {
1256         try {
1257             return new PhylogenyWriter().toPhyloXML( this, phyloxml_level ).toString();
1258         }
1259         catch ( final IOException e ) {
1260             throw new Error( "this should not have happend: " + e.getMessage() );
1261         }
1262     }
1263
1264     // ---------------------------------------------------------
1265     // Writing of Phylogeny to Strings
1266     // ---------------------------------------------------------
1267     /**
1268      * Converts this Phylogeny to a New Hampshire X (String) representation.
1269      * 
1270      * @return New Hampshire X (String) representation of this
1271      * @see #toNewHampshireX()
1272      */
1273     @Override
1274     public String toString() {
1275         return toNewHampshireX();
1276     }
1277
1278     /**
1279      * Removes the root PhylogenyNode this Phylogeny.
1280      */
1281     public void unRoot() throws RuntimeException {
1282         if ( !isTree() ) {
1283             throw new FailedConditionCheckException( "Attempt to unroot a phylogeny which is not tree-like." );
1284         }
1285         if ( isEmpty() ) {
1286             return;
1287         }
1288         setIndicatorsToZero();
1289         if ( !isRooted() || ( getNumberOfExternalNodes() <= 1 ) ) {
1290             return;
1291         }
1292         setRooted( false );
1293         return;
1294     } // unRoot()
1295 }