inprogress
[jalview.git] / forester / java / src / org / forester / msa_compactor / MsaCompactor.java
1 // $Id:
2 // FORESTER -- software libraries and applications
3 // for evolutionary biology research and applications.
4 //
5 // Copyright (C) 2014 Christian M. Zmasek
6 // Copyright (C) 2014 Sanford-Burnham Medical Research Institute
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 // WWW: https://sites.google.com/site/cmzmasek/home/software/forester
24
25 package org.forester.msa_compactor;
26
27 import java.awt.Color;
28 import java.io.File;
29 import java.io.IOException;
30 import java.io.Writer;
31 import java.math.RoundingMode;
32 import java.text.DecimalFormat;
33 import java.text.NumberFormat;
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import java.util.List;
37 import java.util.SortedSet;
38 import java.util.TreeSet;
39
40 import org.forester.archaeopteryx.Archaeopteryx;
41 import org.forester.archaeopteryx.Configuration;
42 import org.forester.evoinference.distance.NeighborJoiningF;
43 import org.forester.evoinference.distance.PairwiseDistanceCalculator;
44 import org.forester.evoinference.distance.PairwiseDistanceCalculator.PWD_DISTANCE_METHOD;
45 import org.forester.evoinference.matrix.distance.BasicSymmetricalDistanceMatrix;
46 import org.forester.evoinference.tools.BootstrapResampler;
47 import org.forester.io.parsers.nhx.NHXParser.TAXONOMY_EXTRACTION;
48 import org.forester.io.parsers.phyloxml.PhyloXmlDataFormatException;
49 import org.forester.io.parsers.util.ParserUtils;
50 import org.forester.io.writers.SequenceWriter;
51 import org.forester.io.writers.SequenceWriter.SEQ_FORMAT;
52 import org.forester.msa.DeleteableMsa;
53 import org.forester.msa.Mafft;
54 import org.forester.msa.Msa;
55 import org.forester.msa.Msa.MSA_FORMAT;
56 import org.forester.msa.MsaInferrer;
57 import org.forester.msa.MsaMethods;
58 import org.forester.msa.ResampleableMsa;
59 import org.forester.phylogeny.Phylogeny;
60 import org.forester.phylogeny.PhylogenyMethods;
61 import org.forester.phylogeny.PhylogenyMethods.DESCENDANT_SORT_PRIORITY;
62 import org.forester.phylogeny.PhylogenyNode;
63 import org.forester.phylogeny.data.NodeVisualData;
64 import org.forester.phylogeny.data.NodeVisualData.NodeFill;
65 import org.forester.phylogeny.data.NodeVisualData.NodeShape;
66 import org.forester.phylogeny.iterators.PhylogenyNodeIterator;
67 import org.forester.sequence.Sequence;
68 import org.forester.tools.ConfidenceAssessor;
69 import org.forester.util.BasicDescriptiveStatistics;
70 import org.forester.util.DescriptiveStatistics;
71 import org.forester.util.ForesterUtil;
72
73 public class MsaCompactor {
74
75     final private static NumberFormat NF_1                       = new DecimalFormat( "0.#" );
76     final private static NumberFormat NF_3                       = new DecimalFormat( "0.###" );
77     final private static NumberFormat NF_4                       = new DecimalFormat( "0.####" );
78     private boolean                   _calculate_shannon_entropy = false;
79     //
80     private String                    _infile_name               = null;
81     private final short               _longest_id_length;
82     //
83     private String                    _maffts_opts               = "--auto";
84     private DeleteableMsa             _msa                       = null;
85     private boolean                   _norm                      = true;
86     private File                      _out_file_base             = null;
87     private MSA_FORMAT                _output_format             = MSA_FORMAT.FASTA;
88     private String                    _path_to_mafft             = null;
89     private boolean                   _phylogentic_inference     = false;
90     //
91     private boolean                   _realign                   = false;
92     private final SortedSet<String>   _removed_seq_ids;
93     private final ArrayList<Sequence> _removed_seqs;
94     private File                      _removed_seqs_out_base     = null;
95     private int                       _step                      = -1;
96     private int                       _step_for_diagnostics      = -1;
97     static {
98         NF_1.setRoundingMode( RoundingMode.HALF_UP );
99         NF_4.setRoundingMode( RoundingMode.HALF_UP );
100         NF_3.setRoundingMode( RoundingMode.HALF_UP );
101     }
102
103     public MsaCompactor( final DeleteableMsa msa ) {
104         _msa = msa;
105         _removed_seq_ids = new TreeSet<String>();
106         _longest_id_length = _msa.determineMaxIdLength();
107         _removed_seqs = new ArrayList<Sequence>();
108     }
109
110     public final Phylogeny calcTree() {
111         final Phylogeny phy = inferNJphylogeny( PWD_DISTANCE_METHOD.KIMURA_DISTANCE, _msa, false, "" );
112         PhylogenyMethods.midpointRoot( phy );
113         PhylogenyMethods.orderAppearance( phy.getRoot(), true, true, DESCENDANT_SORT_PRIORITY.NODE_NAME );
114         final boolean x = PhylogenyMethods.extractFastaInformation( phy );
115         if ( !x ) {
116             final PhylogenyNodeIterator it = phy.iteratorExternalForward();
117             while ( it.hasNext() ) {
118                 final PhylogenyNode n = it.next();
119                 final String name = n.getName().trim();
120                 if ( !ForesterUtil.isEmpty( name ) ) {
121                     try {
122                         ParserUtils.extractTaxonomyDataFromNodeName( n, TAXONOMY_EXTRACTION.AGGRESSIVE );
123                     }
124                     catch ( final PhyloXmlDataFormatException e ) {
125                         // Ignore.
126                     }
127                 }
128             }
129         }
130         return phy;
131     }
132
133     public final List<MsaProperties> chart( final int step, final boolean realign, final boolean norm )
134             throws IOException, InterruptedException {
135         final GapContribution stats[] = calcGapContribtionsStats( norm );
136         final List<String> to_remove_ids = new ArrayList<String>();
137         final List<MsaProperties> msa_props = new ArrayList<MsaProperties>();
138         for( final GapContribution gap_gontribution : stats ) {
139             to_remove_ids.add( gap_gontribution.getId() );
140         }
141         Phylogeny phy = null;
142         if ( _phylogentic_inference ) {
143             System.out.println( "calculating phylogentic tree..." );
144             System.out.println();
145             phy = calcTree();
146         }
147         if ( !_realign ) {
148             _step = -1;
149         }
150         int x = ForesterUtil.roundToInt( _msa.getNumberOfSequences() / 10.0 );
151         if ( x < 2 ) {
152             x = 2;
153         }
154         MsaProperties msa_prop = new MsaProperties( _msa, "", _calculate_shannon_entropy );
155         msa_props.add( msa_prop );
156         printTableHeader();
157         printMsaProperties( msa_prop );
158         System.out.println();
159         int i = 0;
160         while ( _msa.getNumberOfSequences() > x ) {
161             final String id = to_remove_ids.get( i );
162             _msa.deleteRow( id, false );
163             if ( realign && isPrintMsaStatsWriteOutfileAndRealign( i ) ) {
164                 removeGapColumns();
165                 realignWithMafft();
166                 msa_prop = new MsaProperties( _msa, id, _calculate_shannon_entropy );
167                 msa_props.add( msa_prop );
168                 printMsaProperties( msa_prop );
169                 System.out.print( "(realigned)" );
170                 System.out.println();
171             }
172             else if ( isPrintMsaStats( i ) ) {
173                 removeGapColumns();
174                 msa_prop = new MsaProperties( _msa, id, _calculate_shannon_entropy );
175                 msa_props.add( msa_prop );
176                 printMsaProperties( msa_prop );
177                 System.out.println();
178             }
179             ++i;
180         }
181         if ( _phylogentic_inference ) {
182             decorateTree( phy, msa_props, true );
183             displayTree( phy );
184         }
185         return msa_props;
186     }
187
188     public final void decorateTree( final Phylogeny phy, final List<MsaProperties> msa_props, final boolean chart_only ) {
189         final BasicDescriptiveStatistics length_stats = new BasicDescriptiveStatistics();
190         for( int i = 0; i < msa_props.size(); ++i ) {
191             final MsaProperties msa_prop = msa_props.get( i );
192             final String id = msa_prop.getRemovedSeq();
193             if ( !ForesterUtil.isEmpty( id ) ) {
194                 length_stats.addValue( msa_prop.getLength() );
195             }
196         }
197         final double mean = length_stats.arithmeticMean();
198         final double min = length_stats.getMin();
199         final double max = length_stats.getMax();
200         final Color min_color = new Color( 0, 255, 0 );
201         final Color max_color = new Color( 255, 0, 0 );
202         final Color mean_color = new Color( 255, 255, 0 );
203         final PhylogenyNodeIterator it = phy.iteratorExternalForward();
204         if ( chart_only ) {
205             while ( it.hasNext() ) {
206                 final NodeVisualData vis = new NodeVisualData();
207                 vis.setFillType( NodeFill.SOLID );
208                 vis.setShape( NodeShape.RECTANGLE );
209                 vis.setNodeColor( min_color );
210                 it.next().getNodeData().setNodeVisualData( vis );
211             }
212         }
213         for( int i = 0; i < msa_props.size(); ++i ) {
214             final MsaProperties msa_prop = msa_props.get( i );
215             final String id = msa_prop.getRemovedSeq();
216             if ( !ForesterUtil.isEmpty( id ) ) {
217                 final PhylogenyNode n = phy.getNode( id );
218                 n.setName( n.getName() + " [" + i + "]" );
219                 if ( !chart_only ) {
220                     final NodeVisualData vis = new NodeVisualData();
221                     vis.setFillType( NodeFill.SOLID );
222                     vis.setShape( NodeShape.RECTANGLE );
223                     vis.setNodeColor( ForesterUtil.calcColor( msa_prop.getLength(), min, max, mean_color, max_color ) );
224                     n.getNodeData().setNodeVisualData( vis );
225                 }
226                 else {
227                     n.getNodeData()
228                             .getNodeVisualData()
229                             .setNodeColor( ForesterUtil.calcColor( msa_prop.getLength(),
230                                                                    min,
231                                                                    max,
232                                                                    mean,
233                                                                    min_color,
234                                                                    max_color,
235                                                                    mean_color ) );
236                 }
237             }
238         }
239     }
240
241     final public void deleteGapColumns( final double max_allowed_gap_ratio ) {
242         _msa.deleteGapColumns( max_allowed_gap_ratio );
243     }
244
245     public final void displayTree( final Phylogeny phy ) {
246         final Configuration config = new Configuration();
247         config.setDisplayAsPhylogram( true );
248         config.setUseStyle( true );
249         config.setDisplayTaxonomyCode( false );
250         config.setDisplayTaxonomyCommonNames( false );
251         config.setDisplayTaxonomyScientificNames( false );
252         config.setDisplaySequenceNames( false );
253         config.setDisplaySequenceSymbols( false );
254         config.setDisplayGeneNames( false );
255         config.setShowScale( true );
256         config.setAddTaxonomyImagesCB( false );
257         config.setBaseFontSize( 9 );
258         config.setBaseFontFamilyName( "Arial" );
259         Archaeopteryx.createApplication( phy, config, _infile_name );
260     }
261
262     final public Msa getMsa() {
263         return _msa;
264     }
265
266     public final void removeSequencesByMinimalLength( final int min_effective_length ) throws IOException {
267         _msa = DeleteableMsa.createInstance( MsaMethods.removeSequencesByMinimalLength( _msa, min_effective_length ) );
268         removeGapColumns();
269         final String s = writeOutfile();
270         final DescriptiveStatistics msa_stats = MsaMethods.calculateEffectiveLengthStatistics( _msa );
271         System.out.println( "Output MSA                           : " + s );
272         System.out.println( "  MSA length                         : " + _msa.getLength() );
273         System.out.println( "  Number of sequences                : " + _msa.getNumberOfSequences() );
274         System.out.println( "  Median sequence length             : " + NF_1.format( msa_stats.median() ) );
275         System.out.println( "  Mean sequence length               : " + NF_1.format( msa_stats.arithmeticMean() ) );
276         System.out.println( "  Max sequence length                : " + ( ( int ) msa_stats.getMax() ) );
277         System.out.println( "  Min sequence length                : " + ( ( int ) msa_stats.getMin() ) );
278         System.out.println( "  Gap ratio                          : " + NF_4.format( MsaMethods.calcGapRatio( _msa ) ) );
279         System.out.println( "  Normalized Shannon Entropy (entn21): "
280                 + NF_4.format( MsaMethods.calcNormalizedShannonsEntropy( 21, _msa ) ) );
281         System.out.println();
282     }
283
284     public final List<MsaProperties> removeViaGapAverage( final double mean_gapiness ) throws IOException,
285             InterruptedException {
286         final GapContribution stats[] = calcGapContribtionsStats( _norm );
287         final List<String> to_remove_ids = new ArrayList<String>();
288         final List<MsaProperties> msa_props = new ArrayList<MsaProperties>();
289         for( final GapContribution gap_gontribution : stats ) {
290             to_remove_ids.add( gap_gontribution.getId() );
291         }
292         Phylogeny phy = null;
293         if ( _phylogentic_inference ) {
294             System.out.println( "calculating phylogentic tree..." );
295             System.out.println();
296             phy = calcTree();
297         }
298         printTableHeader();
299         MsaProperties msa_prop = new MsaProperties( _msa, "", _calculate_shannon_entropy );
300         msa_props.add( msa_prop );
301         printMsaProperties( msa_prop );
302         System.out.println();
303         int i = 0;
304         while ( MsaMethods.calcGapRatio( _msa ) > mean_gapiness ) {
305             final String id = to_remove_ids.get( i );
306             _removed_seq_ids.add( id );
307             final Sequence deleted = _msa.deleteRow( id, true );
308             _removed_seqs.add( deleted );
309             removeGapColumns();
310             if ( isPrintMsaStatsWriteOutfileAndRealign( i ) || ( MsaMethods.calcGapRatio( _msa ) <= mean_gapiness ) ) {
311                 msa_prop = printMsaStatsWriteOutfileAndRealign( _realign, id );
312                 msa_props.add( msa_prop );
313                 System.out.println();
314             }
315             else if ( isPrintMsaStats( i ) ) {
316                 msa_prop = new MsaProperties( _msa, id, _calculate_shannon_entropy );
317                 msa_props.add( msa_prop );
318                 printMsaProperties( msa_prop );
319                 System.out.println();
320             }
321             ++i;
322         }
323         if ( _removed_seqs_out_base != null ) {
324             final String msg = writeAndAlignRemovedSeqs();
325             System.out.println();
326             System.out.println( msg );
327         }
328         if ( _phylogentic_inference ) {
329             decorateTree( phy, msa_props, false );
330             displayTree( phy );
331         }
332         return msa_props;
333     }
334
335     public List<MsaProperties> removeViaLength( final int length ) throws IOException, InterruptedException {
336         final GapContribution stats[] = calcGapContribtionsStats( _norm );
337         final List<String> to_remove_ids = new ArrayList<String>();
338         final List<MsaProperties> msa_props = new ArrayList<MsaProperties>();
339         for( final GapContribution gap_gontribution : stats ) {
340             to_remove_ids.add( gap_gontribution.getId() );
341         }
342         Phylogeny phy = null;
343         if ( _phylogentic_inference ) {
344             System.out.println( "calculating phylogentic tree..." );
345             System.out.println();
346             phy = calcTree();
347         }
348         printTableHeader();
349         MsaProperties msa_prop = new MsaProperties( _msa, "", _calculate_shannon_entropy );
350         msa_props.add( msa_prop );
351         printMsaProperties( msa_prop );
352         System.out.println();
353         int i = 0;
354         while ( _msa.getLength() > length ) {
355             final String id = to_remove_ids.get( i );
356             _removed_seq_ids.add( id );
357             final Sequence deleted = _msa.deleteRow( id, true );
358             _removed_seqs.add( deleted );
359             removeGapColumns();
360             if ( isPrintMsaStatsWriteOutfileAndRealign( i ) || ( _msa.getLength() <= length ) ) {
361                 msa_prop = printMsaStatsWriteOutfileAndRealign( _realign, id );
362                 msa_props.add( msa_prop );
363                 System.out.println();
364             }
365             else if ( isPrintMsaStats( i ) ) {
366                 msa_prop = new MsaProperties( _msa, id, _calculate_shannon_entropy );
367                 printMsaProperties( msa_prop );
368                 msa_props.add( msa_prop );
369                 System.out.println();
370             }
371             ++i;
372         }
373         if ( _removed_seqs_out_base != null ) {
374             final String msg = writeAndAlignRemovedSeqs();
375             System.out.println();
376             System.out.println( msg );
377         }
378         if ( _phylogentic_inference ) {
379             decorateTree( phy, msa_props, false );
380             displayTree( phy );
381         }
382         return msa_props;
383     }
384
385     public final List<MsaProperties> removeWorstOffenders( final int to_remove ) throws IOException,
386             InterruptedException {
387         final GapContribution stats[] = calcGapContribtionsStats( _norm );
388         final List<String> to_remove_ids = new ArrayList<String>();
389         final List<MsaProperties> msa_props = new ArrayList<MsaProperties>();
390         for( int j = 0; j < to_remove; ++j ) {
391             to_remove_ids.add( stats[ j ].getId() );
392         }
393         Phylogeny phy = null;
394         if ( _phylogentic_inference ) {
395             System.out.println( "calculating phylogentic tree..." );
396             System.out.println();
397             phy = calcTree();
398         }
399         printTableHeader();
400         MsaProperties msa_prop = new MsaProperties( _msa, "", _calculate_shannon_entropy );
401         msa_props.add( msa_prop );
402         printMsaProperties( msa_prop );
403         System.out.println();
404         for( int i = 0; i < to_remove_ids.size(); ++i ) {
405             final String id = to_remove_ids.get( i );
406             _removed_seq_ids.add( id );
407             final Sequence deleted = _msa.deleteRow( id, true );
408             _removed_seqs.add( deleted );
409             removeGapColumns();
410             if ( isPrintMsaStatsWriteOutfileAndRealign( i ) || ( i == ( to_remove_ids.size() - 1 ) ) ) {
411                 msa_prop = printMsaStatsWriteOutfileAndRealign( _realign, id );
412                 msa_props.add( msa_prop );
413                 System.out.println();
414             }
415             else if ( isPrintMsaStats( i ) ) {
416                 msa_prop = new MsaProperties( _msa, id, _calculate_shannon_entropy );
417                 msa_props.add( msa_prop );
418                 printMsaProperties( msa_prop );
419                 System.out.println();
420             }
421         }
422         if ( _removed_seqs_out_base != null ) {
423             final String msg = writeAndAlignRemovedSeqs();
424             System.out.println();
425             System.out.println( msg );
426         }
427         if ( _phylogentic_inference ) {
428             decorateTree( phy, msa_props, false );
429             displayTree( phy );
430         }
431         return msa_props;
432     }
433
434     public final void setCalculateNormalizedShannonEntropy( final boolean calculate_shannon_entropy ) {
435         _calculate_shannon_entropy = calculate_shannon_entropy;
436     }
437
438     public void setInfileName( final String infile_name ) {
439         _infile_name = infile_name;
440     }
441
442     public final void setMafftOptions( final String maffts_opts ) {
443         _maffts_opts = maffts_opts;
444     }
445
446     public final void setNorm( final boolean norm ) {
447         _norm = norm;
448     }
449
450     final public void setOutFileBase( final File out_file_base ) {
451         _out_file_base = out_file_base;
452     }
453
454     public final void setOutputFormat( final MSA_FORMAT output_format ) {
455         _output_format = output_format;
456     }
457
458     public void setPathToMafft( final String path_to_mafft ) {
459         _path_to_mafft = path_to_mafft;
460     }
461
462     public void setPeformPhylogenticInference( final boolean phylogentic_inference ) {
463         _phylogentic_inference = phylogentic_inference;
464     }
465
466     public final void setRealign( final boolean realign ) {
467         _realign = realign;
468     }
469
470     public final void setRemovedSeqsOutBase( final File removed_seqs_out_base ) {
471         _removed_seqs_out_base = removed_seqs_out_base;
472     }
473
474     public final void setStep( final int step ) {
475         _step = step;
476     }
477
478     public final void setStepForDiagnostics( final int step_for_diagnostics ) {
479         _step_for_diagnostics = step_for_diagnostics;
480     }
481
482     final public String writeAndAlignRemovedSeqs() throws IOException, InterruptedException {
483         final StringBuilder msg = new StringBuilder();
484         final String n = _removed_seqs_out_base + "_" + _removed_seqs.size() + ".fasta";
485         SequenceWriter.writeSeqs( _removed_seqs, new File( n ), SEQ_FORMAT.FASTA, 100 );
486         msg.append( "wrote " + _removed_seqs.size() + " removed sequences to " + "\"" + n + "\"" );
487         if ( _realign ) {
488             final MsaInferrer mafft = Mafft.createInstance( _path_to_mafft );
489             final List<String> opts = new ArrayList<String>();
490             for( final String o : _maffts_opts.split( "\\s" ) ) {
491                 opts.add( o );
492             }
493             final Msa removed_msa = mafft.infer( _removed_seqs, opts );
494             final Double gr = MsaMethods.calcGapRatio( removed_msa );
495             String s = _removed_seqs_out_base + "_" + removed_msa.getNumberOfSequences() + "_"
496                     + removed_msa.getLength() + "_" + ForesterUtil.roundToInt( gr * 100 );
497             final String suffix = obtainSuffix();
498             s += suffix;
499             writeMsa( removed_msa, s, _output_format );
500             msg.append( ", and as MSA of length " + removed_msa.getLength() + " to \"" + s + "\"" );
501         }
502         return msg.toString();
503     }
504
505     final public String writeMsa( final File outfile ) throws IOException {
506         final Double gr = MsaMethods.calcGapRatio( _msa );
507         final String s = outfile + "_" + _msa.getNumberOfSequences() + "_" + _msa.getLength() + "_"
508                 + ForesterUtil.roundToInt( gr * 100 );
509         writeMsa( _msa, s + obtainSuffix(), _output_format );
510         return s;
511     }
512
513     final int calcNonGapResidues( final Sequence seq ) {
514         int ng = 0;
515         for( int i = 0; i < seq.getLength(); ++i ) {
516             if ( !seq.isGapAt( i ) ) {
517                 ++ng;
518             }
519         }
520         return ng;
521     }
522
523     private final GapContribution[] calcGapContribtions( final boolean normalize_for_effective_seq_length ) {
524         final double gappiness[] = calcGappiness();
525         final GapContribution stats[] = new GapContribution[ _msa.getNumberOfSequences() ];
526         for( int row = 0; row < _msa.getNumberOfSequences(); ++row ) {
527             stats[ row ] = new GapContribution( _msa.getIdentifier( row ) );
528             for( int col = 0; col < _msa.getLength(); ++col ) {
529                 if ( !_msa.isGapAt( row, col ) ) {
530                     stats[ row ].addToValue( gappiness[ col ] );
531                 }
532             }
533             if ( normalize_for_effective_seq_length ) {
534                 stats[ row ].divideValue( calcNonGapResidues( _msa.getSequence( row ) ) );
535             }
536             else {
537                 stats[ row ].divideValue( _msa.getLength() );
538             }
539         }
540         return stats;
541     }
542
543     final private GapContribution[] calcGapContribtionsStats( final boolean norm ) {
544         final GapContribution stats[] = calcGapContribtions( norm );
545         Arrays.sort( stats );
546         return stats;
547     }
548
549     private final double[] calcGappiness() {
550         final int l = _msa.getLength();
551         final double gappiness[] = new double[ l ];
552         final int seqs = _msa.getNumberOfSequences();
553         for( int i = 0; i < l; ++i ) {
554             gappiness[ i ] = ( double ) MsaMethods.calcGapSumPerColumn( _msa, i ) / seqs;
555         }
556         return gappiness;
557     }
558
559     private final Phylogeny inferNJphylogeny( final PWD_DISTANCE_METHOD pwd_distance_method,
560                                               final Msa msa,
561                                               final boolean write_matrix,
562                                               final String matrix_name ) {
563         BasicSymmetricalDistanceMatrix m = null;
564         switch ( pwd_distance_method ) {
565             case KIMURA_DISTANCE:
566                 m = PairwiseDistanceCalculator.calcKimuraDistances( msa );
567                 break;
568             case POISSON_DISTANCE:
569                 m = PairwiseDistanceCalculator.calcPoissonDistances( msa );
570                 break;
571             case FRACTIONAL_DISSIMILARITY:
572                 m = PairwiseDistanceCalculator.calcFractionalDissimilarities( msa );
573                 break;
574             default:
575                 throw new IllegalArgumentException( "invalid pwd method" );
576         }
577         if ( write_matrix ) {
578             try {
579                 m.write( ForesterUtil.createBufferedWriter( matrix_name ) );
580             }
581             catch ( final IOException e ) {
582                 e.printStackTrace();
583             }
584         }
585         final NeighborJoiningF nj = NeighborJoiningF.createInstance( false, 5 );
586         final Phylogeny phy = nj.execute( m );
587         return phy;
588     }
589
590     private final boolean isPrintMsaStats( final int i ) {
591         return ( ( ( _step == 1 ) && ( _step_for_diagnostics == 1 ) ) || ( ( _step_for_diagnostics > 0 ) && ( ( ( i + 1 ) % _step_for_diagnostics ) == 0 ) ) );
592     }
593
594     private final boolean isPrintMsaStatsWriteOutfileAndRealign( final int i ) {
595         return ( ( ( _step == 1 ) && ( _step_for_diagnostics == 1 ) ) || ( ( _step > 0 ) && ( ( ( i + 1 ) % _step ) == 0 ) ) );
596     }
597
598     private final StringBuilder msaPropertiesAsSB( final MsaProperties msa_properties ) {
599         final StringBuilder sb = new StringBuilder();
600         sb.append( msa_properties.getNumberOfSequences() );
601         sb.append( "\t" );
602         sb.append( msa_properties.getLength() );
603         sb.append( "\t" );
604         sb.append( NF_4.format( msa_properties.getGapRatio() ) );
605         if ( _calculate_shannon_entropy ) {
606             sb.append( "\t" );
607             sb.append( NF_4.format( msa_properties.getEntropy7() ) );
608             sb.append( "\t" );
609             sb.append( NF_4.format( msa_properties.getEntropy21() ) );
610         }
611         return sb;
612     }
613
614     private String obtainSuffix() {
615         if ( _output_format == MSA_FORMAT.FASTA ) {
616             return ".fasta";
617         }
618         else if ( _output_format == MSA_FORMAT.PHYLIP ) {
619             return ".aln";
620         }
621         return "";
622     }
623
624     private final Phylogeny pi( final String matrix, final int boostrap ) {
625         final Phylogeny master_phy = inferNJphylogeny( PWD_DISTANCE_METHOD.KIMURA_DISTANCE, _msa, true, matrix );
626         final int seed = 15;
627         final int n = 100;
628         final ResampleableMsa resampleable_msa = new ResampleableMsa( _msa );
629         final int[][] resampled_column_positions = BootstrapResampler.createResampledColumnPositions( _msa.getLength(),
630                                                                                                       n,
631                                                                                                       seed );
632         final Phylogeny[] eval_phys = new Phylogeny[ n ];
633         for( int i = 0; i < n; ++i ) {
634             resampleable_msa.resample( resampled_column_positions[ i ] );
635             eval_phys[ i ] = inferNJphylogeny( PWD_DISTANCE_METHOD.KIMURA_DISTANCE, resampleable_msa, false, null );
636         }
637         ConfidenceAssessor.evaluate( "bootstrap", eval_phys, master_phy, true, 1 );
638         PhylogenyMethods.extractFastaInformation( master_phy );
639         return master_phy;
640     }
641
642     private final void printMsaProperties( final MsaProperties msa_properties ) {
643         if ( ( _step == 1 ) || ( _step_for_diagnostics == 1 ) ) {
644             System.out.print( ForesterUtil.pad( msa_properties.getRemovedSeq(), _longest_id_length, ' ', false ) );
645             System.out.print( "\t" );
646         }
647         System.out.print( msaPropertiesAsSB( msa_properties ) );
648         System.out.print( "\t" );
649     }
650
651     final private MsaProperties printMsaStatsWriteOutfileAndRealign( final boolean realign, final String id )
652             throws IOException, InterruptedException {
653         if ( realign ) {
654             realignWithMafft();
655         }
656         final MsaProperties msa_prop = new MsaProperties( _msa, id, _calculate_shannon_entropy );
657         printMsaProperties( msa_prop );
658         final String s = writeOutfile();
659         System.out.print( "-> " + s + ( realign ? "\t(realigned)" : "" ) );
660         return msa_prop;
661     }
662
663     private final void printTableHeader() {
664         if ( ( _step == 1 ) || ( _step_for_diagnostics == 1 ) ) {
665             System.out.print( ForesterUtil.pad( "Id", _longest_id_length, ' ', false ) );
666             System.out.print( "\t" );
667         }
668         System.out.print( "Seqs" );
669         System.out.print( "\t" );
670         System.out.print( "Length" );
671         System.out.print( "\t" );
672         System.out.print( "Gaps" );
673         System.out.print( "\t" );
674         if ( _calculate_shannon_entropy ) {
675             System.out.print( "entn7" );
676             System.out.print( "\t" );
677             System.out.print( "entn21" );
678             System.out.print( "\t" );
679         }
680         System.out.println();
681     }
682
683     final private void realignWithMafft() throws IOException, InterruptedException {
684         final MsaInferrer mafft = Mafft.createInstance( _path_to_mafft );
685         final List<String> opts = new ArrayList<String>();
686         for( final String o : _maffts_opts.split( "\\s" ) ) {
687             opts.add( o );
688         }
689         _msa = DeleteableMsa.createInstance( mafft.infer( _msa.asSequenceList(), opts ) );
690     }
691
692     final private void removeGapColumns() {
693         _msa.deleteGapOnlyColumns();
694     }
695
696     private final String writeOutfile() throws IOException {
697         final String s = writeMsa( _out_file_base );
698         return s;
699     }
700
701     // Returns null if not path found.
702     final public static String guessPathToMafft() {
703         String path;
704         if ( ForesterUtil.OS_NAME.toLowerCase().indexOf( "win" ) >= 0 ) {
705             path = "C:\\Program Files\\mafft-win\\mafft.bat";
706             if ( MsaInferrer.isInstalled( path ) ) {
707                 return path;
708             }
709         }
710         path = "/home/czmasek/SOFTWARE/MSA/MAFFT/mafft-7.130-without-extensions/scripts/mafft";
711         if ( MsaInferrer.isInstalled( path ) ) {
712             return path;
713         }
714         path = "/usr/local/bin/mafft";
715         if ( MsaInferrer.isInstalled( path ) ) {
716             return path;
717         }
718         path = "/usr/bin/mafft";
719         if ( MsaInferrer.isInstalled( path ) ) {
720             return path;
721         }
722         path = "/bin/mafft";
723         if ( MsaInferrer.isInstalled( path ) ) {
724             return path;
725         }
726         path = "mafft";
727         if ( MsaInferrer.isInstalled( path ) ) {
728             return path;
729         }
730         return null;
731     }
732
733     final private static void writeMsa( final Msa msa, final String outfile, final MSA_FORMAT format )
734             throws IOException {
735         final Writer w = ForesterUtil.createBufferedWriter( outfile );
736         msa.write( w, format );
737         w.close();
738     }
739 }