improving GSDI, under construction...
[jalview.git] / forester / java / src / org / forester / sdi / GSDI.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 // All rights reserved
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 //
23 // Contact: phylosoft @ gmail . com
24 // WWW: www.phylosoft.org/forester
25
26 package org.forester.sdi;
27
28 import java.util.ArrayList;
29 import java.util.HashMap;
30 import java.util.HashSet;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Set;
34
35 import org.forester.phylogeny.Phylogeny;
36 import org.forester.phylogeny.PhylogenyNode;
37 import org.forester.phylogeny.data.Event;
38 import org.forester.phylogeny.data.Taxonomy;
39 import org.forester.phylogeny.iterators.PhylogenyNodeIterator;
40 import org.forester.util.ForesterUtil;
41
42 /*
43  * Implements our algorithm for speciation - duplication inference (SDI). <p>
44  * The initialization is accomplished by: </p> <ul> <li>method
45  * "linkExtNodesOfG()" of class SDI: setting the links for the external nodes of
46  * the gene tree <li>"preorderReID(int)" from class Phylogeny: numbering of
47  * nodes of the species tree in preorder <li>the optional stripping of the
48  * species tree is accomplished by method "stripTree(Phylogeny,Phylogeny)" of
49  * class Phylogeny </ul> <p> The recursion part is accomplished by this class'
50  * method "geneTreePostOrderTraversal(PhylogenyNode)". <p> Requires JDK 1.5 or
51  * greater.
52  * 
53  * @see SDI#linkNodesOfG()
54  * 
55  * @see Phylogeny#preorderReID(int)
56  * 
57  * @see
58  * PhylogenyMethods#taxonomyBasedDeletionOfExternalNodes(Phylogeny,Phylogeny)
59  * 
60  * @see #geneTreePostOrderTraversal(PhylogenyNode)
61  * 
62  * @author Christian M. Zmasek
63  */
64 public final class GSDI extends SDI {
65
66     private final boolean             _most_parsimonious_duplication_model;
67     private final boolean             _strip_gene_tree;
68     private final boolean             _strip_species_tree;
69     private int                       _speciation_or_duplication_events_sum;
70     private int                       _speciations_sum;
71     private final List<PhylogenyNode> _stripped_gene_tree_nodes;
72     private final List<PhylogenyNode> _stripped_species_tree_nodes;
73     private final Set<PhylogenyNode>  _mapped_species_tree_nodes;
74
75     /**
76      * Constructor which sets the gene tree and the species tree to be compared.
77      * species_tree is the species tree to which the gene tree gene_tree will be
78      * compared to - with method "infer(boolean)". Both Trees must be completely
79      * binary and rooted. The actual inference is accomplished with method
80      * "infer(boolean)". The mapping cost L can then be calculated with method
81      * "computeMappingCost()".
82      * <p>
83      * 
84      * @see #infer(boolean)
85      * @see SDI#computeMappingCostL()
86      * @param gene_tree
87      *            reference to a rooted gene tree to which assign duplication vs
88      *            speciation, must have species names in the species name fields
89      *            for all external nodes
90      * @param species_tree
91      *            reference to a rooted binary species tree which might get
92      *            stripped in the process, must have species names in the
93      *            species name fields for all external nodes
94      * 
95      * @param most_parsimonious_duplication_model
96      *            set to true to assign nodes as speciations which would
97      *            otherwise be assiged as unknown because of polytomies in the
98      *            species tree.
99      * @throws SdiException 
100      * 
101      */
102     public GSDI( final Phylogeny gene_tree,
103                  final Phylogeny species_tree,
104                  final boolean most_parsimonious_duplication_model,
105                  final boolean strip_gene_tree,
106                  final boolean strip_species_tree ) throws SdiException {
107         super( gene_tree, species_tree );
108         _speciation_or_duplication_events_sum = 0;
109         _speciations_sum = 0;
110         _most_parsimonious_duplication_model = most_parsimonious_duplication_model;
111         _duplications_sum = 0;
112         _strip_gene_tree = strip_gene_tree;
113         _strip_species_tree = strip_species_tree;
114         _stripped_gene_tree_nodes = new ArrayList<PhylogenyNode>();
115         _stripped_species_tree_nodes = new ArrayList<PhylogenyNode>();
116         _mapped_species_tree_nodes = new HashSet<PhylogenyNode>();
117         getSpeciesTree().preOrderReId();
118         linkNodesOfG();
119         //geneTreePostOrderTraversal( getGeneTree().getRoot(), null );
120         geneTreePostOrderTraversal();
121     }
122
123     GSDI( final Phylogeny gene_tree, final Phylogeny species_tree, final boolean most_parsimonious_duplication_model )
124             throws SdiException {
125         this( gene_tree, species_tree, most_parsimonious_duplication_model, false, false );
126     }
127
128     // s is the node on the species tree g maps to.
129     private final void determineEvent( final PhylogenyNode s, final PhylogenyNode g ) {
130         Event event = null;
131         // Determine how many children map to same node as parent.
132         int sum_g_childs_mapping_to_s = 0;
133         for( int i = 0; i < g.getNumberOfDescendants(); ++i ) {
134             final PhylogenyNode c = g.getChildNode( i );
135             if ( c.getLink() == s ) {
136                 ++sum_g_childs_mapping_to_s;
137             }
138         }
139         if ( g.getLink().getNumberOfDescendants() == 2 ) {
140             if ( sum_g_childs_mapping_to_s > 0 ) {
141                 event = createDuplicationEvent();
142             }
143             else {
144                 event = createSpeciationEvent();
145             }
146         }
147         else {
148             if ( sum_g_childs_mapping_to_s > 0 ) {
149                 boolean multiple = false;
150                 Set<PhylogenyNode> set = new HashSet<PhylogenyNode>();
151                 for( PhylogenyNode n : g.getChildNode1().getLink().getAllExternalDescendants() ) {
152                     set.add( n );
153                 }
154                 for( PhylogenyNode n : g.getChildNode2().getLink().getAllExternalDescendants() ) {
155                     if ( set.contains( n ) ) {
156                         multiple = true;
157                         break;
158                     }
159                     // else {
160                     //     set.add( n );
161                     // }
162                 }
163                 if ( multiple ) {
164                     event = createDuplicationEvent();
165                 }
166                 else {
167                     event = createSingleSpeciationOrDuplicationEvent();
168                 }
169             }
170             else {
171                 event = createSpeciationEvent();
172             }
173         }
174         g.getNodeData().setEvent( event );
175     }
176
177     //    private final void determineEvent2( final PhylogenyNode s, final PhylogenyNode g ) {
178     //        Event event = null;
179     //        // Determine how many children map to same node as parent.
180     //        int sum_g_childs_mapping_to_s = 0;
181     //        for( int i = 0; i < g.getNumberOfDescendants(); ++i ) {
182     //            final PhylogenyNode c = g.getChildNode( i );
183     //            if ( c.getLink() == s ) {
184     //                ++sum_g_childs_mapping_to_s;
185     //            }
186     //        }
187     //        // Determine the sum of traversals.
188     //        int traversals_sum = 0;
189     //        int max_traversals = 0;
190     //        PhylogenyNode max_traversals_node = null;
191     //        if ( !s.isExternal() ) {
192     //            for( int i = 0; i < s.getNumberOfDescendants(); ++i ) {
193     //                final PhylogenyNode current_node = s.getChildNode( i );
194     //                final int traversals = getTraversalCount( current_node );
195     //                traversals_sum += traversals;
196     //                if ( traversals > max_traversals ) {
197     //                    max_traversals = traversals;
198     //                    max_traversals_node = current_node;
199     //                }
200     //            }
201     //        }
202     //        // System.out.println( " sum=" + traversals_sum );
203     //        // System.out.println( " max=" + max_traversals );
204     //        // System.out.println( " m=" + sum_g_childs_mapping_to_s );
205     //        if ( s.getNumberOfDescendants() == 2 ) {
206     //            if ( sum_g_childs_mapping_to_s == 0 ) {
207     //                event = createSpeciationEvent();
208     //            }
209     //            else {
210     //                event = createDuplicationEvent();
211     //            }
212     //        }
213     //        else {
214     //            if ( sum_g_childs_mapping_to_s == 2 ) {
215     //                event = createDuplicationEvent();
216     //            }
217     //            else if ( sum_g_childs_mapping_to_s == 1 ) {
218     //                event = createSingleSpeciationOrDuplicationEvent();
219     //            }
220     //            else {
221     //                event = createSpeciationEvent();
222     //            }
223     //        }
224     //        g.getNodeData().setEvent( event );
225     //    }
226     /**
227      * Traverses the subtree of PhylogenyNode g in postorder, calculating the
228      * mapping function M, and determines which nodes represent speciation
229      * events and which ones duplication events.
230      * <p>
231      * Preconditions: Mapping M for external nodes must have been calculated and
232      * the species tree must be labeled in preorder.
233      * <p>
234      * 
235      * @param g
236      *            starting node of a gene tree - normally the root
237      */
238     final void geneTreePostOrderTraversal() {
239         for( PhylogenyNodeIterator it = getGeneTree().iteratorPostorder(); it.hasNext(); ) {
240             PhylogenyNode g = it.next();
241             if ( !g.isExternal() ) {
242                 final PhylogenyNode[] linked_nodes = new PhylogenyNode[ g.getNumberOfDescendants() ];
243                 for( int i = 0; i < linked_nodes.length; ++i ) {
244                     if ( g.getChildNode( i ).getLink() == null ) {
245                         System.out.println( "link is null for " + g.getChildNode( i ) );
246                         System.exit( -1 );
247                     }
248                     linked_nodes[ i ] = g.getChildNode( i ).getLink();
249                 }
250                 final int[] min_max = obtainMinMaxIdIndices( linked_nodes );
251                 int min_i = min_max[ 0 ];
252                 int max_i = min_max[ 1 ];
253                 while ( linked_nodes[ min_i ] != linked_nodes[ max_i ] ) {
254                     linked_nodes[ max_i ] = linked_nodes[ max_i ].getParent();
255                     final int[] min_max_ = obtainMinMaxIdIndices( linked_nodes );
256                     min_i = min_max_[ 0 ];
257                     max_i = min_max_[ 1 ];
258                 }
259                 final PhylogenyNode s = linked_nodes[ max_i ];
260                 g.setLink( s );
261                 // Determines whether dup. or spec.
262                 determineEvent( s, g );
263             }
264         }
265     }
266
267     private final Event createDuplicationEvent() {
268         final Event event = Event.createSingleDuplicationEvent();
269         ++_duplications_sum;
270         return event;
271     }
272
273     private final Event createSingleSpeciationOrDuplicationEvent() {
274         final Event event = Event.createSingleSpeciationOrDuplicationEvent();
275         ++_speciation_or_duplication_events_sum;
276         return event;
277     }
278
279     private final Event createSpeciationEvent() {
280         final Event event = Event.createSingleSpeciationEvent();
281         ++_speciations_sum;
282         return event;
283     }
284
285     public final int getSpeciationOrDuplicationEventsSum() {
286         return _speciation_or_duplication_events_sum;
287     }
288
289     public final int getSpeciationsSum() {
290         return _speciations_sum;
291     }
292
293     /**
294      * This allows for linking of internal nodes of the species tree (as opposed
295      * to just external nodes, as in the method it overrides.
296      * @throws SdiException 
297      * 
298      */
299     @Override
300     //    final void linkNodesOfG() {
301     //        final HashMap<Taxonomy, PhylogenyNode> speciestree_ext_nodes = createTaxonomyToNodeMap();
302     //        if ( _strip_gene_tree ) {
303     //            stripGeneTree( speciestree_ext_nodes );
304     //            if ( ( _gene_tree == null ) || ( _gene_tree.getNumberOfExternalNodes() < 2 ) ) {
305     //                throw new IllegalArgumentException( "species tree does not contain any"
306     //                        + " nodes matching species in the gene tree" );
307     //            }
308     //        }
309     //        // Retrieve the reference to the PhylogenyNode with a matching species.
310     //        for( final PhylogenyNodeIterator iter = _gene_tree.iteratorExternalForward(); iter.hasNext(); ) {
311     //            final PhylogenyNode g = iter.next();
312     //            if ( !g.getNodeData().isHasTaxonomy() ) {
313     //                throw new IllegalArgumentException( "gene tree node " + g + " has no taxonomic data" );
314     //            }
315     //            final PhylogenyNode s = speciestree_ext_nodes.get( g.getNodeData().getTaxonomy() );
316     //            if ( s == null ) {
317     //                throw new IllegalArgumentException( "species " + g.getNodeData().getTaxonomy()
318     //                        + " not present in species tree" );
319     //            }
320     //            g.setLink( s );
321     //        }
322     //    }
323     final void linkNodesOfG() throws SdiException {
324         final Map<String, PhylogenyNode> species_to_node_map = new HashMap<String, PhylogenyNode>();
325         final List<PhylogenyNode> species_tree_ext_nodes = new ArrayList<PhylogenyNode>();
326         final TaxonomyComparisonBase tax_comp_base = determineTaxonomyComparisonBase( _gene_tree );
327         // System.out.println( "comp base is: " + tax_comp_base );
328         // Stringyfied taxonomy is the key, node is the value.
329         for( final PhylogenyNodeIterator iter = _species_tree.iteratorExternalForward(); iter.hasNext(); ) {
330             final PhylogenyNode s = iter.next();
331             species_tree_ext_nodes.add( s );
332             final String tax_str = taxonomyToString( s, tax_comp_base );
333             if ( !ForesterUtil.isEmpty( tax_str ) ) {
334                 if ( species_to_node_map.containsKey( tax_str ) ) {
335                     throw new SdiException( "taxonomy \"" + s + "\" is not unique in species tree" );
336                 }
337                 species_to_node_map.put( tax_str, s );
338             }
339         }
340         // Retrieve the reference to the node with a matching stringyfied taxonomy.
341         for( final PhylogenyNodeIterator iter = _gene_tree.iteratorExternalForward(); iter.hasNext(); ) {
342             final PhylogenyNode g = iter.next();
343             if ( !g.getNodeData().isHasTaxonomy() ) {
344                 if ( _strip_gene_tree ) {
345                     _stripped_gene_tree_nodes.add( g );
346                 }
347                 else {
348                     throw new SdiException( "gene tree node \"" + g + "\" has no taxonomic data" );
349                 }
350             }
351             else {
352                 final String tax_str = taxonomyToString( g, tax_comp_base );
353                 if ( ForesterUtil.isEmpty( tax_str ) ) {
354                     if ( _strip_gene_tree ) {
355                         _stripped_gene_tree_nodes.add( g );
356                     }
357                     else {
358                         throw new SdiException( "gene tree node \"" + g + "\" has no appropriate taxonomic data" );
359                     }
360                 }
361                 else {
362                     final PhylogenyNode s = species_to_node_map.get( tax_str );
363                     if ( s == null ) {
364                         if ( _strip_gene_tree ) {
365                             _stripped_gene_tree_nodes.add( g );
366                         }
367                         else {
368                             throw new SdiException( "taxonomy \"" + g.getNodeData().getTaxonomy()
369                                     + "\" not present in species tree" );
370                         }
371                     }
372                     else {
373                         g.setLink( s );
374                         _mapped_species_tree_nodes.add( s );
375                         //  System.out.println( "setting link of " + g + " to " + s );
376                     }
377                 }
378             }
379         } // for loop
380         if ( _strip_gene_tree ) {
381             for( final PhylogenyNode g : _stripped_gene_tree_nodes ) {
382                 _gene_tree.deleteSubtree( g, true );
383             }
384         }
385         if ( _strip_species_tree ) {
386             for( final PhylogenyNode s : species_tree_ext_nodes ) {
387                 if ( !_mapped_species_tree_nodes.contains( s ) ) {
388                     _species_tree.deleteSubtree( s, true );
389                 }
390             }
391         }
392     }
393
394     public Set<PhylogenyNode> getMappedExternalSpeciesTreeNodes() {
395         return _mapped_species_tree_nodes;
396     }
397
398     //    final private HashMap<Taxonomy, PhylogenyNode> createTaxonomyToNodeMap() {
399     //        final HashMap<Taxonomy, PhylogenyNode> speciestree_ext_nodes = new HashMap<Taxonomy, PhylogenyNode>();
400     //        for( final PhylogenyNodeIterator iter = _species_tree.iteratorLevelOrder(); iter.hasNext(); ) {
401     //            final PhylogenyNode n = iter.next();
402     //            if ( n.getNodeData().isHasTaxonomy() ) {
403     //                if ( speciestree_ext_nodes.containsKey( n.getNodeData().getTaxonomy() ) ) {
404     //                    throw new IllegalArgumentException( "taxonomy [" + n.getNodeData().getTaxonomy()
405     //                            + "] is not unique in species phylogeny" );
406     //                }
407     //                speciestree_ext_nodes.put( n.getNodeData().getTaxonomy(), n );
408     //            }
409     //        }
410     //        return speciestree_ext_nodes;
411     //    }
412     //    private final void stripGeneTree( final HashMap<Taxonomy, PhylogenyNode> speciestree_ext_nodes ) {
413     //        //  final Set<PhylogenyNode> to_delete = new HashSet<PhylogenyNode>();
414     //        for( final PhylogenyNodeIterator iter = _gene_tree.iteratorExternalForward(); iter.hasNext(); ) {
415     //            final PhylogenyNode g = iter.next();
416     //            if ( !g.getNodeData().isHasTaxonomy() ) {
417     //                throw new IllegalArgumentException( "gene tree node " + g + " has no taxonomic data" );
418     //            }
419     //            if ( !speciestree_ext_nodes.containsKey( g.getNodeData().getTaxonomy() ) ) {
420     //                _stripped_gene_tree_nodes.add( g );
421     //            }
422     //        }
423     //        for( final PhylogenyNode n : _stripped_gene_tree_nodes ) {
424     //            _gene_tree.deleteSubtree( n, true );
425     //        }
426     //    }
427     //    private final void stripGeneTree2( final HashMap<Taxonomy, PhylogenyNode> speciestree_ext_nodes ) {
428     //        //  final Set<PhylogenyNode> to_delete = new HashSet<PhylogenyNode>();
429     //        for( final PhylogenyNodeIterator iter = _gene_tree.iteratorExternalForward(); iter.hasNext(); ) {
430     //            final PhylogenyNode g = iter.next();
431     //            if ( !g.getNodeData().isHasTaxonomy() ) {
432     //                _stripped_gene_tree_nodes.add( g );
433     //            }
434     //            else {
435     //                if ( !speciestree_ext_nodes.containsKey( g.getNodeData().getTaxonomy() ) ) {
436     //                    _stripped_gene_tree_nodes.add( g );
437     //                }
438     //            }
439     //        }
440     //        for( final PhylogenyNode n : _stripped_gene_tree_nodes ) {
441     //            _gene_tree.deleteSubtree( n, true );
442     //        }
443     //    }
444     public static TaxonomyComparisonBase determineTaxonomyComparisonBase( final Phylogeny gene_tree ) {
445         int with_id_count = 0;
446         int with_code_count = 0;
447         int with_sn_count = 0;
448         int max = 0;
449         for( final PhylogenyNodeIterator iter = gene_tree.iteratorExternalForward(); iter.hasNext(); ) {
450             final PhylogenyNode g = iter.next();
451             if ( g.getNodeData().isHasTaxonomy() ) {
452                 final Taxonomy tax = g.getNodeData().getTaxonomy();
453                 if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() ) ) {
454                     if ( ++with_id_count > max ) {
455                         max = with_id_count;
456                     }
457                 }
458                 if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
459                     if ( ++with_code_count > max ) {
460                         max = with_code_count;
461                     }
462                 }
463                 if ( !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
464                     if ( ++with_sn_count > max ) {
465                         max = with_sn_count;
466                     }
467                 }
468             }
469         }
470         if ( max == 0 ) {
471             throw new IllegalArgumentException( "gene tree has no taxonomic data" );
472         }
473         else if ( max == 1 ) {
474             throw new IllegalArgumentException( "gene tree has only one node with taxonomic data" );
475         }
476         else if ( max == with_sn_count ) {
477             return SDI.TaxonomyComparisonBase.SCIENTIFIC_NAME;
478         }
479         else if ( max == with_id_count ) {
480             return SDI.TaxonomyComparisonBase.ID;
481         }
482         else {
483             return SDI.TaxonomyComparisonBase.CODE;
484         }
485     }
486
487     public List<PhylogenyNode> getStrippedExternalGeneTreeNodes() {
488         return _stripped_gene_tree_nodes;
489     }
490
491     @Override
492     public final String toString() {
493         final StringBuffer sb = new StringBuffer();
494         sb.append( "Most parsimonious duplication model: " + _most_parsimonious_duplication_model );
495         sb.append( ForesterUtil.getLineSeparator() );
496         sb.append( "Speciations sum                    : " + getSpeciationsSum() );
497         sb.append( ForesterUtil.getLineSeparator() );
498         sb.append( "Duplications sum                   : " + getDuplicationsSum() );
499         sb.append( ForesterUtil.getLineSeparator() );
500         if ( !_most_parsimonious_duplication_model ) {
501             sb.append( "Speciation or duplications sum     : " + getSpeciationOrDuplicationEventsSum() );
502             sb.append( ForesterUtil.getLineSeparator() );
503         }
504         sb.append( "mapping cost L                     : " + computeMappingCostL() );
505         return sb.toString();
506     }
507
508     static final int[] obtainMinMaxIdIndices( final PhylogenyNode[] linked_nodes ) {
509         int max_i = 0;
510         int min_i = 0;
511         int max_i_id = -Integer.MAX_VALUE;
512         int min_i_id = Integer.MAX_VALUE;
513         for( int i = 0; i < linked_nodes.length; ++i ) {
514             final int id_i = linked_nodes[ i ].getId();
515             if ( id_i > max_i_id ) {
516                 max_i = i;
517                 max_i_id = linked_nodes[ max_i ].getId();
518             }
519             if ( id_i < min_i_id ) {
520                 min_i = i;
521                 min_i_id = linked_nodes[ min_i ].getId();
522             }
523         }
524         return new int[] { min_i, max_i };
525     }
526     /**
527      * Updates the mapping function M after the root of the gene tree has been
528      * moved by one branch. It calculates M for the root of the gene tree and
529      * one of its two children.
530      * <p>
531      * To be used ONLY by method "SDIunrooted.fastInfer(Phylogeny,Phylogeny)".
532      * <p>
533      * (Last modfied: )
534      * 
535      * @param prev_root_was_dup
536      *            true if the previous root was a duplication, false otherwise
537      * @param prev_root_c1
538      *            child 1 of the previous root
539      * @param prev_root_c2
540      *            child 2 of the previous root
541      * @return number of duplications which have been assigned in gene tree
542      */
543     // int updateM( final boolean prev_root_was_dup,
544     // final PhylogenyNode prev_root_c1, final PhylogenyNode prev_root_c2 ) {
545     // final PhylogenyNode root = getGeneTree().getRoot();
546     // if ( ( root.getChildNode1() == prev_root_c1 )
547     // || ( root.getChildNode2() == prev_root_c1 ) ) {
548     // calculateMforNode( prev_root_c1 );
549     // }
550     // else {
551     // calculateMforNode( prev_root_c2 );
552     // }
553     // Event event = null;
554     // if ( prev_root_was_dup ) {
555     // event = Event.createSingleDuplicationEvent();
556     // }
557     // else {
558     // event = Event.createSingleSpeciationEvent();
559     // }
560     // root.getPhylogenyNodeData().setEvent( event );
561     // calculateMforNode( root );
562     // return getDuplications();
563     // } // updateM( boolean, PhylogenyNode, PhylogenyNode )
564     // Helper method for updateM( boolean, PhylogenyNode, PhylogenyNode )
565     // Calculates M for PhylogenyNode n, given that M for the two children
566     // of n has been calculated.
567     // (Last modified: 10/02/01)
568     // private void calculateMforNode( final PhylogenyNode n ) {
569     // if ( !n.isExternal() ) {
570     // boolean was_duplication = n.isDuplication();
571     // PhylogenyNode a = n.getChildNode1().getLink(), b = n
572     // .getChildNode2().getLink();
573     // while ( a != b ) {
574     // if ( a.getID() > b.getID() ) {
575     // a = a.getParent();
576     // }
577     // else {
578     // b = b.getParent();
579     // }
580     // }
581     // n.setLink( a );
582     // Event event = null;
583     // if ( ( a == n.getChildNode1().getLink() )
584     // || ( a == n.getChildNode2().getLink() ) ) {
585     // event = Event.createSingleDuplicationEvent();
586     // if ( !was_duplication ) {
587     // increaseDuplications();
588     // }
589     // }
590     // else {
591     // event = Event.createSingleSpeciationEvent();
592     // if ( was_duplication ) {
593     // decreaseDuplications();
594     // }
595     // }
596     // n.getPhylogenyNodeData().setEvent( event );
597     // }
598     // } // calculateMforNode( PhylogenyNode )
599 }