"rio" work
[jalview.git] / forester / java / src / org / forester / rio / RIO.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.rio;
29
30 import java.io.File;
31 import java.io.FileNotFoundException;
32 import java.io.IOException;
33 import java.util.ArrayList;
34 import java.util.Collections;
35 import java.util.HashMap;
36 import java.util.HashSet;
37 import java.util.List;
38 import java.util.Set;
39 import java.util.SortedSet;
40 import java.util.TreeSet;
41
42 import org.forester.datastructures.IntMatrix;
43 import org.forester.io.parsers.PhylogenyParser;
44 import org.forester.io.parsers.nhx.NHXParser;
45 import org.forester.io.parsers.util.ParserUtils;
46 import org.forester.phylogeny.Phylogeny;
47 import org.forester.phylogeny.PhylogenyMethods;
48 import org.forester.phylogeny.PhylogenyNode;
49 import org.forester.phylogeny.data.Taxonomy;
50 import org.forester.phylogeny.factories.ParserBasedPhylogenyFactory;
51 import org.forester.phylogeny.factories.PhylogenyFactory;
52 import org.forester.sdi.GSDI;
53 import org.forester.sdi.GSDIR;
54 import org.forester.sdi.SDIException;
55 import org.forester.sdi.SDIR;
56 import org.forester.sdi.SDIutil.ALGORITHM;
57 import org.forester.sdi.SDIutil.TaxonomyComparisonBase;
58 import org.forester.util.BasicDescriptiveStatistics;
59 import org.forester.util.ForesterUtil;
60
61 public final class RIO {
62
63     public static final int                  DEFAULT_RANGE = -1;
64     private Phylogeny[]                      _analyzed_gene_trees;
65     private List<PhylogenyNode>              _removed_gene_tree_nodes;
66     private int                              _ext_nodes;
67     private TaxonomyComparisonBase           _gsdir_tax_comp_base;
68     private final StringBuilder              _log;
69     private final BasicDescriptiveStatistics _duplications_stats;
70     private final boolean                    _produce_log;
71     private final boolean                    _verbose;
72     private final REROOTING                  _rerooting;
73
74     private RIO( final Phylogeny[] gene_trees,
75                  final Phylogeny species_tree,
76                  final ALGORITHM algorithm,
77                  final REROOTING rerooting,
78                  final String outgroup,
79                  int first,
80                  int last,
81                  final boolean produce_log,
82                  final boolean verbose ) throws IOException, SDIException, RIOException {
83         if ( ( last == DEFAULT_RANGE ) && ( first >= 0 ) ) {
84             last = gene_trees.length - 1;
85         }
86         else if ( ( first == DEFAULT_RANGE ) && ( last >= 0 ) ) {
87             first = 0;
88         }
89         checkPreconditions( gene_trees, rerooting, outgroup, first, last );
90         _produce_log = produce_log;
91         _verbose = verbose;
92         _rerooting = rerooting;
93         _ext_nodes = -1;
94         _log = new StringBuilder();
95         _gsdir_tax_comp_base = null;
96         _analyzed_gene_trees = null;
97         _removed_gene_tree_nodes = null;
98         _duplications_stats = new BasicDescriptiveStatistics();
99         inferOrthologs( gene_trees, species_tree, algorithm, outgroup, first, last );
100     }
101
102     public final Phylogeny[] getAnalyzedGeneTrees() {
103         return _analyzed_gene_trees;
104     }
105
106     public final BasicDescriptiveStatistics getDuplicationsStatistics() {
107         return _duplications_stats;
108     }
109
110     /**
111      * Returns the numbers of number of ext nodes in gene trees analyzed (after
112      * stripping).
113      * 
114      * @return number of ext nodes in gene trees analyzed (after stripping)
115      */
116     public final int getExtNodesOfAnalyzedGeneTrees() {
117         return _ext_nodes;
118     }
119
120     public final TaxonomyComparisonBase getGSDIRtaxCompBase() {
121         return _gsdir_tax_comp_base;
122     }
123
124     public final StringBuilder getLog() {
125         return _log;
126     }
127
128     public final List<PhylogenyNode> getRemovedGeneTreeNodes() {
129         return _removed_gene_tree_nodes;
130     }
131
132     private final void inferOrthologs( final Phylogeny[] gene_trees,
133                                        final Phylogeny species_tree,
134                                        final ALGORITHM algorithm,
135                                        final String outgroup,
136                                        final int first,
137                                        final int last ) throws SDIException, RIOException, FileNotFoundException,
138             IOException {
139         if ( algorithm == ALGORITHM.SDIR ) {
140             // Removes from species_tree all species not found in gene_tree.
141             PhylogenyMethods.taxonomyBasedDeletionOfExternalNodes( gene_trees[ 0 ], species_tree );
142             if ( species_tree.isEmpty() ) {
143                 throw new RIOException( "failed to establish species based mapping between gene and species trees" );
144             }
145         }
146         final Phylogeny[] my_gene_trees;
147         if ( ( first >= 0 ) && ( last >= first ) && ( last < gene_trees.length ) ) {
148             my_gene_trees = new Phylogeny[ 1 + last - first ];
149             int c = 0;
150             for( int i = first; i <= last; ++i ) {
151                 my_gene_trees[ c++ ] = gene_trees[ i ];
152             }
153         }
154         else {
155             my_gene_trees = gene_trees;
156         }
157         if ( log() ) {
158             preLog( gene_trees, species_tree, algorithm, outgroup, first, last );
159         }
160         if ( _verbose && ( my_gene_trees.length >= 4 ) ) {
161             System.out.println();
162         }
163         _analyzed_gene_trees = new Phylogeny[ my_gene_trees.length ];
164         int gene_tree_ext_nodes = 0;
165         for( int i = 0; i < my_gene_trees.length; ++i ) {
166             final Phylogeny gt = my_gene_trees[ i ];
167             if ( _verbose && ( my_gene_trees.length > 4 ) ) {
168                 ForesterUtil.updateProgress( ( ( double ) i ) / my_gene_trees.length );
169             }
170             if ( i == 0 ) {
171                 gene_tree_ext_nodes = gt.getNumberOfExternalNodes();
172             }
173             else if ( gene_tree_ext_nodes != gt.getNumberOfExternalNodes() ) {
174                 throw new RIOException( "gene tree #" + ( i + 1 ) + " has a different number of external nodes ("
175                         + gt.getNumberOfExternalNodes() + ") than the preceding gene trees (" + gene_tree_ext_nodes
176                         + ")" );
177             }
178             if ( algorithm == ALGORITHM.SDIR ) {
179                 // Removes from gene_tree all species not found in species_tree.
180                 PhylogenyMethods.taxonomyBasedDeletionOfExternalNodes( species_tree, gt );
181                 if ( gt.isEmpty() ) {
182                     throw new RIOException( "failed to establish species based mapping between gene and species trees" );
183                 }
184             }
185             _analyzed_gene_trees[ i ] = performOrthologInference( gt, species_tree, algorithm, outgroup, i );
186         }
187         if ( log() ) {
188             postLog( species_tree );
189         }
190         if ( _verbose && ( my_gene_trees.length > 4 ) ) {
191             System.out.println();
192             System.out.println();
193         }
194     }
195
196     private final boolean log() {
197         return _produce_log;
198     }
199
200     private final void log( final String s ) {
201         _log.append( s );
202         _log.append( ForesterUtil.LINE_SEPARATOR );
203     }
204
205     private final void logRemovedGeneTreeNodes() {
206         log( "Species stripped from gene trees:" );
207         final SortedSet<String> rn = new TreeSet<String>();
208         for( final PhylogenyNode n : getRemovedGeneTreeNodes() ) {
209             final Taxonomy t = n.getNodeData().getTaxonomy();
210             switch ( getGSDIRtaxCompBase() ) {
211                 case CODE: {
212                     rn.add( t.getTaxonomyCode() );
213                     break;
214                 }
215                 case ID: {
216                     rn.add( t.getIdentifier().toString() );
217                     break;
218                 }
219                 case SCIENTIFIC_NAME: {
220                     rn.add( t.getScientificName() );
221                     break;
222                 }
223             }
224         }
225         for( final String s : rn ) {
226             log( s );
227         }
228         log( "" );
229     }
230
231     private final Phylogeny performOrthologInference( final Phylogeny gene_tree,
232                                                       final Phylogeny species_tree,
233                                                       final ALGORITHM algorithm,
234                                                       final String outgroup,
235                                                       final int i ) throws SDIException, RIOException {
236         final Phylogeny assigned_tree;
237         switch ( algorithm ) {
238             case SDIR: {
239                 assigned_tree = performOrthologInferenceBySDI( gene_tree, species_tree );
240                 break;
241             }
242             case GSDIR: {
243                 assigned_tree = performOrthologInferenceByGSDI( gene_tree, species_tree, outgroup, i );
244                 break;
245             }
246             default: {
247                 throw new IllegalArgumentException( "illegal algorithm: " + algorithm );
248             }
249         }
250         if ( i == 0 ) {
251             _ext_nodes = assigned_tree.getNumberOfExternalNodes();
252         }
253         else if ( _ext_nodes != assigned_tree.getNumberOfExternalNodes() ) {
254             throw new RIOException( "after stripping gene tree #" + ( i + 1 )
255                     + " has a different number of external nodes (" + assigned_tree.getNumberOfExternalNodes()
256                     + ") than the preceding gene trees (" + _ext_nodes + ")" );
257         }
258         return assigned_tree;
259     }
260
261     private final Phylogeny performOrthologInferenceByGSDI( final Phylogeny gene_tree,
262                                                             final Phylogeny species_tree,
263                                                             final String outgroup,
264                                                             final int i ) throws SDIException, RIOException {
265         final Phylogeny assigned_tree;
266         if ( _rerooting == REROOTING.BY_ALGORITHM ) {
267             final GSDIR gsdir = new GSDIR( gene_tree, species_tree, true, i == 0 );
268             final List<Phylogeny> assigned_trees = gsdir.getMinDuplicationsSumGeneTrees();
269             if ( i == 0 ) {
270                 _removed_gene_tree_nodes = gsdir.getStrippedExternalGeneTreeNodes();
271                 for( final PhylogenyNode r : _removed_gene_tree_nodes ) {
272                     if ( !r.getNodeData().isHasTaxonomy() ) {
273                         throw new RIOException( "node with no (appropriate) taxonomic information found in gene tree #1: "
274                                 + r.toString() );
275                     }
276                 }
277             }
278             final List<Integer> shortests = GSDIR.getIndexesOfShortestTree( assigned_trees );
279             assigned_tree = assigned_trees.get( shortests.get( 0 ) );
280             if ( log() ) {
281                 writeStatsToLog( i, gsdir, shortests );
282             }
283             if ( i == 0 ) {
284                 _gsdir_tax_comp_base = gsdir.getTaxCompBase();
285             }
286             _duplications_stats.addValue( gsdir.getMinDuplicationsSum() );
287         }
288         else {
289             if ( _rerooting == REROOTING.MIDPOINT ) {
290                 PhylogenyMethods.midpointRoot( gene_tree );
291             }
292             else if ( _rerooting == REROOTING.OUTGROUP ) {
293                 final PhylogenyNode n = gene_tree.getNode( outgroup );
294                 gene_tree.reRoot( n );
295             }
296             final GSDI gsdi = new GSDI( gene_tree, species_tree, true, true, true );
297             _removed_gene_tree_nodes = gsdi.getStrippedExternalGeneTreeNodes();
298             for( final PhylogenyNode r : _removed_gene_tree_nodes ) {
299                 if ( !r.getNodeData().isHasTaxonomy() ) {
300                     throw new RIOException( "node with no (appropriate) taxonomic information found in gene tree #1: "
301                             + r.toString() );
302                 }
303             }
304             assigned_tree = gene_tree;
305             if ( i == 0 ) {
306                 _gsdir_tax_comp_base = gsdi.getTaxCompBase();
307             }
308             _duplications_stats.addValue( gsdi.getDuplicationsSum() );
309         }
310         return assigned_tree;
311     }
312
313     private final Phylogeny performOrthologInferenceBySDI( final Phylogeny gene_tree, final Phylogeny species_tree )
314             throws SDIException {
315         final SDIR sdir = new SDIR();
316         return sdir.infer( gene_tree, species_tree, false, true, true, true, 1 )[ 0 ];
317     }
318
319     private final void postLog( final Phylogeny species_tree ) {
320         log( "" );
321         if ( getRemovedGeneTreeNodes().size() > 0 ) {
322             logRemovedGeneTreeNodes();
323         }
324         log( "Species tree external nodes (after stripping)   : " + species_tree.getNumberOfExternalNodes() );
325         log( "Species tree polytomies (after stripping)       : "
326                 + PhylogenyMethods.countNumberOfPolytomies( species_tree ) );
327         log( "Taxonomy linking based on                       : " + getGSDIRtaxCompBase() );
328         final java.text.DecimalFormat df = new java.text.DecimalFormat( "0.#" );
329         log( "Gene trees analyzed                             : " + _duplications_stats.getN() );
330         log( "Mean number of duplications                     : " + df.format( _duplications_stats.arithmeticMean() )
331                 + " (sd: " + df.format( _duplications_stats.sampleStandardDeviation() ) + ")" );
332         if ( _duplications_stats.getN() > 3 ) {
333             log( "Median number of duplications                   : " + df.format( _duplications_stats.median() ) );
334         }
335         log( "Minimum duplications                            : " + ( int ) _duplications_stats.getMin() );
336         log( "Maximum duplications                            : " + ( int ) _duplications_stats.getMax() );
337     }
338
339     private final void preLog( final Phylogeny[] gene_trees,
340                                final Phylogeny species_tree,
341                                final ALGORITHM algorithm,
342                                final String outgroup,
343                                final int first,
344                                final int last ) {
345         log( "Number of gene tree (total)                     : " + gene_trees.length );
346         log( "Algorithm                                       : " + algorithm );
347         log( "Species tree external nodes (prior to stripping): " + species_tree.getNumberOfExternalNodes() );
348         log( "Species tree polytomies (prior to stripping)    : "
349                 + PhylogenyMethods.countNumberOfPolytomies( species_tree ) );
350         String rs = "";
351         switch ( _rerooting ) {
352             case BY_ALGORITHM: {
353                 rs = "minimizing duplications";
354                 break;
355             }
356             case MIDPOINT: {
357                 rs = "midpoint";
358                 break;
359             }
360             case OUTGROUP: {
361                 rs = "outgroup: " + outgroup;
362                 break;
363             }
364             case NONE: {
365                 rs = "none";
366                 break;
367             }
368         }
369         log( "Re-rooting                                      : " + rs );
370         if ( ( first >= 0 ) || ( last >= 0 ) ) {
371             log( "Gene trees analyzed range                       : " + first + "-" + last );
372         }
373         if ( _rerooting == REROOTING.BY_ALGORITHM ) {
374             writeLogSubHeader();
375         }
376     }
377
378     private final void writeLogSubHeader() {
379         _log.append( ForesterUtil.LINE_SEPARATOR );
380         _log.append( "Some information about duplication numbers in gene trees:" );
381         _log.append( ForesterUtil.LINE_SEPARATOR );
382         _log.append( "#" );
383         _log.append( "\t" );
384         _log.append( "re-rootings with minimal number of duplications" );
385         _log.append( "/" );
386         _log.append( "total root placements" );
387         _log.append( "\t" );
388         _log.append( "duplications range" );
389         _log.append( "\t" );
390         _log.append( "mininal duplication re-rootings with shortest tree heigth" );
391         _log.append( ForesterUtil.LINE_SEPARATOR );
392     }
393
394     private final void writeStatsToLog( final int i, final GSDIR gsdir, final List<Integer> shortests ) {
395         final BasicDescriptiveStatistics stats = gsdir.getDuplicationsSumStats();
396         _log.append( i );
397         _log.append( "\t" );
398         _log.append( gsdir.getMinDuplicationsSumGeneTrees().size() );
399         _log.append( "/" );
400         _log.append( stats.getN() );
401         _log.append( "\t" );
402         _log.append( ( int ) stats.getMin() );
403         _log.append( "-" );
404         _log.append( ( int ) stats.getMax() );
405         _log.append( "\t" );
406         _log.append( shortests.size() );
407         _log.append( ForesterUtil.LINE_SEPARATOR );
408     }
409
410     public final static IntMatrix calculateOrthologTable( final Phylogeny[] analyzed_gene_trees, final boolean sort )
411             throws RIOException {
412         final List<String> labels = new ArrayList<String>();
413         final Set<String> labels_set = new HashSet<String>();
414         String label;
415         for( final PhylogenyNode n : analyzed_gene_trees[ 0 ].getExternalNodes() ) {
416             if ( n.getNodeData().isHasSequence() && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getName() ) ) {
417                 label = n.getNodeData().getSequence().getName();
418             }
419             else if ( n.getNodeData().isHasSequence()
420                     && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getSymbol() ) ) {
421                 label = n.getNodeData().getSequence().getSymbol();
422             }
423             else if ( !ForesterUtil.isEmpty( n.getName() ) ) {
424                 label = n.getName();
425             }
426             else {
427                 throw new RIOException( "node " + n + " has no appropriate label" );
428             }
429             if ( labels_set.contains( label ) ) {
430                 throw new RIOException( "label " + label + " is not unique" );
431             }
432             labels_set.add( label );
433             labels.add( label );
434         }
435         if ( sort ) {
436             Collections.sort( labels );
437         }
438         final IntMatrix m = new IntMatrix( labels );
439         int counter = 0;
440         for( final Phylogeny gt : analyzed_gene_trees ) {
441             counter++;
442             PhylogenyMethods.preOrderReId( gt );
443             final HashMap<String, PhylogenyNode> map = PhylogenyMethods.createNameToExtNodeMap( gt );
444             for( int x = 0; x < m.size(); ++x ) {
445                 final String mx = m.getLabel( x );
446                 final PhylogenyNode nx = map.get( mx );
447                 if ( nx == null ) {
448                     throw new RIOException( "node \"" + mx + "\" not present in gene tree #" + counter );
449                 }
450                 String my;
451                 PhylogenyNode ny;
452                 for( int y = 0; y < m.size(); ++y ) {
453                     my = m.getLabel( y );
454                     ny = map.get( my );
455                     if ( ny == null ) {
456                         throw new RIOException( "node \"" + my + "\" not present in gene tree #" + counter );
457                     }
458                     if ( !PhylogenyMethods.calculateLCAonTreeWithIdsInPreOrder( nx, ny ).isDuplication() ) {
459                         m.inreaseByOne( x, y );
460                     }
461                 }
462             }
463         }
464         return m;
465     }
466
467     public final static RIO executeAnalysis( final File gene_trees_file,
468                                              final Phylogeny species_tree,
469                                              final ALGORITHM algorithm,
470                                              final REROOTING rerooting,
471                                              final String outgroup,
472                                              final boolean produce_log,
473                                              final boolean verbose ) throws IOException, SDIException, RIOException {
474         final PhylogenyFactory factory = ParserBasedPhylogenyFactory.getInstance();
475         final PhylogenyParser p = ParserUtils.createParserDependingOnFileType( gene_trees_file, true );
476         if ( p instanceof NHXParser ) {
477             final NHXParser nhx = ( NHXParser ) p;
478             nhx.setReplaceUnderscores( false );
479             nhx.setIgnoreQuotes( true );
480             nhx.setTaxonomyExtraction( NHXParser.TAXONOMY_EXTRACTION.YES );
481         }
482         final Phylogeny[] gene_trees = factory.create( gene_trees_file, p );
483         return new RIO( gene_trees,
484                         species_tree,
485                         algorithm,
486                         rerooting,
487                         outgroup,
488                         DEFAULT_RANGE,
489                         DEFAULT_RANGE,
490                         produce_log,
491                         verbose );
492     }
493
494     public final static RIO executeAnalysis( final File gene_trees_file,
495                                              final Phylogeny species_tree,
496                                              final ALGORITHM algorithm,
497                                              final REROOTING rerooting,
498                                              final String outgroup,
499                                              final int first,
500                                              final int last,
501                                              final boolean produce_log,
502                                              final boolean verbose ) throws IOException, SDIException, RIOException {
503         final PhylogenyFactory factory = ParserBasedPhylogenyFactory.getInstance();
504         final PhylogenyParser p = ParserUtils.createParserDependingOnFileType( gene_trees_file, true );
505         if ( p instanceof NHXParser ) {
506             final NHXParser nhx = ( NHXParser ) p;
507             nhx.setReplaceUnderscores( false );
508             nhx.setIgnoreQuotes( true );
509             nhx.setTaxonomyExtraction( NHXParser.TAXONOMY_EXTRACTION.YES );
510         }
511         final Phylogeny[] gene_trees = factory.create( gene_trees_file, p );
512         return new RIO( gene_trees, species_tree, algorithm, rerooting, outgroup, first, last, produce_log, verbose );
513     }
514
515     public final static RIO executeAnalysis( final Phylogeny[] gene_trees, final Phylogeny species_tree )
516             throws IOException, SDIException, RIOException {
517         return new RIO( gene_trees,
518                         species_tree,
519                         ALGORITHM.GSDIR,
520                         REROOTING.BY_ALGORITHM,
521                         null,
522                         DEFAULT_RANGE,
523                         DEFAULT_RANGE,
524                         false,
525                         false );
526     }
527
528     public final static RIO executeAnalysis( final Phylogeny[] gene_trees,
529                                              final Phylogeny species_tree,
530                                              final ALGORITHM algorithm,
531                                              final REROOTING rerooting,
532                                              final String outgroup,
533                                              final boolean produce_log,
534                                              final boolean verbose ) throws IOException, SDIException, RIOException {
535         return new RIO( gene_trees,
536                         species_tree,
537                         algorithm,
538                         rerooting,
539                         outgroup,
540                         DEFAULT_RANGE,
541                         DEFAULT_RANGE,
542                         produce_log,
543                         verbose );
544     }
545
546     public final static RIO executeAnalysis( final Phylogeny[] gene_trees,
547                                              final Phylogeny species_tree,
548                                              final ALGORITHM algorithm,
549                                              final REROOTING rerooting,
550                                              final String outgroup,
551                                              final int first,
552                                              final int last,
553                                              final boolean produce_log,
554                                              final boolean verbose ) throws IOException, SDIException, RIOException {
555         return new RIO( gene_trees, species_tree, algorithm, rerooting, outgroup, first, last, produce_log, verbose );
556     }
557
558     private final static void checkPreconditions( final Phylogeny[] gene_trees,
559                                                   final REROOTING rerooting,
560                                                   final String outgroup,
561                                                   final int first,
562                                                   final int last ) throws RIOException {
563         if ( !( ( last == DEFAULT_RANGE ) && ( first == DEFAULT_RANGE ) )
564                 && ( ( last < first ) || ( last >= gene_trees.length ) || ( last < 0 ) || ( first < 0 ) ) ) {
565             throw new RIOException( "attempt to set range (0-based) of gene to analyze to: from " + first + " to "
566                     + last + " (out of " + gene_trees.length + ")" );
567         }
568         if ( ( rerooting == REROOTING.OUTGROUP ) && ForesterUtil.isEmpty( outgroup ) ) {
569             throw new RIOException( "outgroup not set for midpoint rooting" );
570         }
571         if ( ( rerooting != REROOTING.OUTGROUP ) && !ForesterUtil.isEmpty( outgroup ) ) {
572             throw new RIOException( "outgroup only used for midpoint rooting" );
573         }
574         if ( ( rerooting == REROOTING.MIDPOINT )
575                 && ( PhylogenyMethods.calculateMaxDistanceToRoot( gene_trees[ 0 ] ) <= 0 ) ) {
576             throw new RIOException( "attempt to use midpoint rooting on gene trees which seem to have no (positive) branch lengths (cladograms)" );
577         }
578         if ( rerooting == REROOTING.OUTGROUP ) {
579             try {
580                 gene_trees[ 0 ].getNode( outgroup );
581             }
582             catch ( final IllegalArgumentException e ) {
583                 throw new RIOException( "cannot perform re-rooting by outgroup: " + e.getLocalizedMessage() );
584             }
585         }
586     }
587
588     public enum REROOTING {
589         NONE, BY_ALGORITHM, MIDPOINT, OUTGROUP;
590     }
591 }