8a13b5cc6bdb2ff28954e78fdb04751ad8c850d9
[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.MolecularSequence;
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                            _normalize_for_effective_seq_length                      = 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<MolecularSequence> _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<MolecularSequence>();
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 normalize_for_effective_seq_length )
134             throws IOException, InterruptedException {
135         final GapContribution stats[] = calcGapContribtionsStats( normalize_for_effective_seq_length );
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             addSeqs2Tree( _msa, phy );
147         }
148         if ( !_realign ) {
149             _step = -1;
150         }
151         int x = ForesterUtil.roundToInt( _msa.getNumberOfSequences() / 10.0 );
152         if ( x < 2 ) {
153             x = 2;
154         }
155         MsaProperties msa_prop = new MsaProperties( _msa, "", _calculate_shannon_entropy );
156         msa_props.add( msa_prop );
157         printTableHeader();
158         printMsaProperties( msa_prop );
159         System.out.println();
160         int i = 0;
161         while ( _msa.getNumberOfSequences() > x ) {
162             final String id = to_remove_ids.get( i );
163             _msa.deleteRow( id, false );
164             if ( realign && isPrintMsaStatsWriteOutfileAndRealign( i ) ) {
165                 removeGapColumns();
166                 realignWithMafft();
167                 msa_prop = new MsaProperties( _msa, id, _calculate_shannon_entropy );
168                 msa_props.add( msa_prop );
169                 printMsaProperties( msa_prop );
170                 System.out.print( "(realigned)" );
171                 System.out.println();
172             }
173             else if ( isPrintMsaStats( i ) ) {
174                 removeGapColumns();
175                 msa_prop = new MsaProperties( _msa, id, _calculate_shannon_entropy );
176                 msa_props.add( msa_prop );
177                 printMsaProperties( msa_prop );
178                 System.out.println();
179             }
180             ++i;
181         }
182         
183         if ( _phylogentic_inference ) {
184             decorateTree( phy, msa_props, true );
185             displayTree( phy );
186         }
187         return msa_props;
188     }
189
190     private final static void addSeqs2Tree( final Msa msa, final Phylogeny phy ) {
191         for( int i = 0; i < msa.getNumberOfSequences(); ++i ) {
192             final MolecularSequence seq = msa.getSequence( i );
193             final String seq_name = seq.getIdentifier();
194             final PhylogenyNode n = phy.getNode( seq_name );
195             if ( !n.getNodeData().isHasSequence() ) {
196                 n.getNodeData().addSequence( new org.forester.phylogeny.data.Sequence() );
197             }
198             else {
199                 throw new IllegalArgumentException( "this should not have happened" );
200             }
201             n.getNodeData().getSequence().setMolecularSequence( seq.getMolecularSequenceAsString() );
202             n.getNodeData().getSequence().setMolecularSequenceAligned( true );
203             n.getNodeData().getSequence().setName( seq_name );
204         }
205     }
206
207     private final static void decorateTree( final Phylogeny phy,
208                                             final List<MsaProperties> msa_props,
209                                             final boolean chart_only ) {
210         final BasicDescriptiveStatistics length_stats = new BasicDescriptiveStatistics();
211         for( int i = 0; i < msa_props.size(); ++i ) {
212             final MsaProperties msa_prop = msa_props.get( i );
213             final String id = msa_prop.getRemovedSeq();
214             if ( !ForesterUtil.isEmpty( id ) ) {
215                 length_stats.addValue( msa_prop.getLength() );
216             }
217         }
218         final double mean = length_stats.arithmeticMean();
219         final double min = length_stats.getMin();
220         final double max = length_stats.getMax();
221         final Color min_color = new Color( 0, 255, 0 );
222         final Color max_color = new Color( 255, 0, 0 );
223         final Color mean_color = new Color( 255, 255, 0 );
224         final PhylogenyNodeIterator it = phy.iteratorExternalForward();
225         if ( chart_only ) {
226             while ( it.hasNext() ) {
227                 final NodeVisualData vis = new NodeVisualData();
228                 vis.setFillType( NodeFill.SOLID );
229                 vis.setShape( NodeShape.RECTANGLE );
230                 vis.setNodeColor( min_color );
231                 it.next().getNodeData().setNodeVisualData( vis );
232             }
233         }
234         for( int i = 0; i < msa_props.size(); ++i ) {
235             final MsaProperties msa_prop = msa_props.get( i );
236             final String id = msa_prop.getRemovedSeq();
237             if ( !ForesterUtil.isEmpty( id ) ) {
238                 final PhylogenyNode n = phy.getNode( id );
239                 n.setName( n.getName() + " [" + i + "]" );
240                 if ( !chart_only ) {
241                     final NodeVisualData vis = new NodeVisualData();
242                     vis.setFillType( NodeFill.SOLID );
243                     vis.setShape( NodeShape.RECTANGLE );
244                     vis.setNodeColor( ForesterUtil.calcColor( msa_prop.getLength(), min, max, mean_color, max_color ) );
245                     n.getNodeData().setNodeVisualData( vis );
246                 }
247                 else {
248                     n.getNodeData()
249                     .getNodeVisualData()
250                     .setNodeColor( ForesterUtil.calcColor( msa_prop.getLength(),
251                                                            min,
252                                                            max,
253                                                            mean,
254                                                            min_color,
255                                                            max_color,
256                                                            mean_color ) );
257                 }
258             }
259         }
260     }
261
262     final public void deleteGapColumns( final double max_allowed_gap_ratio ) {
263         _msa.deleteGapColumns( max_allowed_gap_ratio );
264     }
265
266     public final void displayTree( final Phylogeny phy ) {
267         final Configuration config = new Configuration();
268         config.setDisplayAsPhylogram( true );
269         config.setUseStyle( true );
270         config.setDisplayTaxonomyCode( false );
271         config.setDisplayTaxonomyCommonNames( false );
272         config.setDisplayTaxonomyScientificNames( false );
273         config.setDisplaySequenceNames( false );
274         config.setDisplaySequenceSymbols( false );
275         config.setDisplayGeneNames( false );
276         config.setDisplayMultipleSequenceAlignment( true );
277         config.setShowScale( true );
278         config.setAddTaxonomyImagesCB( false );
279         config.setBaseFontSize( 9 );
280         config.setBaseFontFamilyName( "Arial" );
281         Archaeopteryx.createApplication( phy, config, _infile_name );
282     }
283
284     final public Msa getMsa() {
285         return _msa;
286     }
287
288     public final void removeSequencesByMinimalLength( final int min_effective_length ) throws IOException {
289         _msa = DeleteableMsa.createInstance( MsaMethods.removeSequencesByMinimalLength( _msa, min_effective_length ) );
290         removeGapColumns();
291         final String s = writeOutfile();
292         final DescriptiveStatistics msa_stats = MsaMethods.calculateEffectiveLengthStatistics( _msa );
293         System.out.println( "Output MSA                           : " + s );
294         System.out.println( "  MSA length                         : " + _msa.getLength() );
295         System.out.println( "  Number of sequences                : " + _msa.getNumberOfSequences() );
296         System.out.println( "  Median sequence length             : " + NF_1.format( msa_stats.median() ) );
297         System.out.println( "  Mean sequence length               : " + NF_1.format( msa_stats.arithmeticMean() ) );
298         System.out.println( "  Max sequence length                : " + ( ( int ) msa_stats.getMax() ) );
299         System.out.println( "  Min sequence length                : " + ( ( int ) msa_stats.getMin() ) );
300         System.out.println( "  Gap ratio                          : " + NF_4.format( MsaMethods.calcGapRatio( _msa ) ) );
301         System.out.println( "  Normalized Shannon Entropy (entn21): "
302                 + NF_4.format( MsaMethods.calcNormalizedShannonsEntropy( 21, _msa ) ) );
303         System.out.println();
304     }
305
306     public final List<MsaProperties> removeViaGapAverage( final double mean_gapiness ) throws IOException,
307     InterruptedException {
308         final GapContribution stats[] = calcGapContribtionsStats( _normalize_for_effective_seq_length );
309         final List<String> to_remove_ids = new ArrayList<String>();
310         final List<MsaProperties> msa_props = new ArrayList<MsaProperties>();
311         for( final GapContribution gap_gontribution : stats ) {
312             to_remove_ids.add( gap_gontribution.getId() );
313         }
314         Phylogeny phy = null;
315         if ( _phylogentic_inference ) {
316             System.out.println( "calculating phylogentic tree..." );
317             System.out.println();
318             phy = calcTree();
319             addSeqs2Tree( _msa, phy );
320         }
321         printTableHeader();
322         MsaProperties msa_prop = new MsaProperties( _msa, "", _calculate_shannon_entropy );
323         msa_props.add( msa_prop );
324         printMsaProperties( msa_prop );
325         System.out.println();
326         int i = 0;
327         while ( MsaMethods.calcGapRatio( _msa ) > mean_gapiness ) {
328             final String id = to_remove_ids.get( i );
329             _removed_seq_ids.add( id );
330             final MolecularSequence deleted = _msa.deleteRow( id, true );
331             _removed_seqs.add( deleted );
332             removeGapColumns();
333             if ( isPrintMsaStatsWriteOutfileAndRealign( i ) || ( MsaMethods.calcGapRatio( _msa ) <= mean_gapiness ) ) {
334                 msa_prop = printMsaStatsWriteOutfileAndRealign( _realign, id );
335                 msa_props.add( msa_prop );
336                 System.out.println();
337             }
338             else if ( isPrintMsaStats( i ) ) {
339                 msa_prop = new MsaProperties( _msa, id, _calculate_shannon_entropy );
340                 msa_props.add( msa_prop );
341                 printMsaProperties( msa_prop );
342                 System.out.println();
343             }
344             ++i;
345         }
346         if ( _removed_seqs_out_base != null ) {
347             final String msg = writeAndAlignRemovedSeqs();
348             System.out.println();
349             System.out.println( msg );
350         }
351         if ( _phylogentic_inference ) {
352             decorateTree( phy, msa_props, false );
353             displayTree( phy );
354             System.out.println( "calculating phylogentic tree..." );
355             System.out.println();
356             final Phylogeny phy2 = calcTree();
357             addSeqs2Tree( _msa, phy2 );
358             displayTree( phy2 );
359         }   
360       
361        
362         return msa_props;
363     }
364
365     public List<MsaProperties> removeViaLength( final int length ) throws IOException, InterruptedException {
366         final GapContribution stats[] = calcGapContribtionsStats( _normalize_for_effective_seq_length );
367         final List<String> to_remove_ids = new ArrayList<String>();
368         final List<MsaProperties> msa_props = new ArrayList<MsaProperties>();
369         for( final GapContribution gap_gontribution : stats ) {
370             to_remove_ids.add( gap_gontribution.getId() );
371         }
372         Phylogeny phy = null;
373         if ( _phylogentic_inference ) {
374             System.out.println( "calculating phylogentic tree..." );
375             System.out.println();
376             phy = calcTree();
377             addSeqs2Tree( _msa, phy );
378         }
379         printTableHeader();
380         MsaProperties msa_prop = new MsaProperties( _msa, "", _calculate_shannon_entropy );
381         msa_props.add( msa_prop );
382         printMsaProperties( msa_prop );
383         System.out.println();
384         int i = 0;
385         while ( _msa.getLength() > length ) {
386             final String id = to_remove_ids.get( i );
387             _removed_seq_ids.add( id );
388             final MolecularSequence deleted = _msa.deleteRow( id, true );
389             _removed_seqs.add( deleted );
390             removeGapColumns();
391             if ( isPrintMsaStatsWriteOutfileAndRealign( i ) || ( _msa.getLength() <= length ) ) {
392                 msa_prop = printMsaStatsWriteOutfileAndRealign( _realign, id );
393                 msa_props.add( msa_prop );
394                 System.out.println();
395             }
396             else if ( isPrintMsaStats( i ) ) {
397                 msa_prop = new MsaProperties( _msa, id, _calculate_shannon_entropy );
398                 printMsaProperties( msa_prop );
399                 msa_props.add( msa_prop );
400                 System.out.println();
401             }
402             ++i;
403         }
404         if ( _removed_seqs_out_base != null ) {
405             final String msg = writeAndAlignRemovedSeqs();
406             System.out.println();
407             System.out.println( msg );
408         }
409         if ( _phylogentic_inference ) {
410             decorateTree( phy, msa_props, false );
411             displayTree( phy );
412             System.out.println( "calculating phylogentic tree..." );
413             System.out.println();
414             final Phylogeny phy2 = calcTree();
415             addSeqs2Tree( _msa, phy2 );
416             displayTree( phy2 );
417         }   
418        
419         return msa_props;
420     }
421
422     public final List<MsaProperties> removeWorstOffenders( final int to_remove ) throws IOException,
423     InterruptedException {
424         final GapContribution stats[] = calcGapContribtionsStats( _normalize_for_effective_seq_length );
425         final List<String> to_remove_ids = new ArrayList<String>();
426         final List<MsaProperties> msa_props = new ArrayList<MsaProperties>();
427         for( int j = 0; j < to_remove; ++j ) {
428             to_remove_ids.add( stats[ j ].getId() );
429         }
430         Phylogeny phy = null;
431         if ( _phylogentic_inference ) {
432             System.out.println( "calculating phylogentic tree..." );
433             System.out.println();
434             phy = calcTree();
435             addSeqs2Tree( _msa, phy );
436         }
437         printTableHeader();
438         MsaProperties msa_prop = new MsaProperties( _msa, "", _calculate_shannon_entropy );
439         msa_props.add( msa_prop );
440         printMsaProperties( msa_prop );
441         System.out.println();
442         for( int i = 0; i < to_remove_ids.size(); ++i ) {
443             final String id = to_remove_ids.get( i );
444             _removed_seq_ids.add( id );
445             final MolecularSequence deleted = _msa.deleteRow( id, true );
446             _removed_seqs.add( deleted );
447             removeGapColumns();
448             if ( isPrintMsaStatsWriteOutfileAndRealign( i ) || ( i == ( to_remove_ids.size() - 1 ) ) ) {
449                 msa_prop = printMsaStatsWriteOutfileAndRealign( _realign, id );
450                 msa_props.add( msa_prop );
451                 System.out.println();
452             }
453             else if ( isPrintMsaStats( i ) ) {
454                 msa_prop = new MsaProperties( _msa, id, _calculate_shannon_entropy );
455                 msa_props.add( msa_prop );
456                 printMsaProperties( msa_prop );
457                 System.out.println();
458             }
459         }
460         if ( _removed_seqs_out_base != null ) {
461             final String msg = writeAndAlignRemovedSeqs();
462             System.out.println();
463             System.out.println( msg );
464         }
465         if ( _phylogentic_inference ) {
466             decorateTree( phy, msa_props, false );
467             displayTree( phy );
468             System.out.println( "calculating phylogentic tree..." );
469             System.out.println();
470             final Phylogeny phy2 = calcTree();
471             addSeqs2Tree( _msa, phy2 );
472             displayTree( phy2 );
473         }   
474        
475         return msa_props;
476     }
477
478     public final void setCalculateNormalizedShannonEntropy( final boolean calculate_shannon_entropy ) {
479         _calculate_shannon_entropy = calculate_shannon_entropy;
480     }
481
482     public void setInfileName( final String infile_name ) {
483         _infile_name = infile_name;
484     }
485
486     public final void setMafftOptions( final String maffts_opts ) {
487         _maffts_opts = maffts_opts;
488     }
489
490     public final void setNorm( final boolean normalize_for_effective_seq_length ) {
491         _normalize_for_effective_seq_length = normalize_for_effective_seq_length;
492     }
493
494     final public void setOutFileBase( final File out_file_base ) {
495         _out_file_base = out_file_base;
496     }
497
498     public final void setOutputFormat( final MSA_FORMAT output_format ) {
499         _output_format = output_format;
500     }
501
502     public void setPathToMafft( final String path_to_mafft ) {
503         _path_to_mafft = path_to_mafft;
504     }
505
506     public void setPeformPhylogenticInference( final boolean phylogentic_inference ) {
507         _phylogentic_inference = phylogentic_inference;
508     }
509
510     public final void setRealign( final boolean realign ) {
511         _realign = realign;
512     }
513
514     public final void setRemovedSeqsOutBase( final File removed_seqs_out_base ) {
515         _removed_seqs_out_base = removed_seqs_out_base;
516     }
517
518     public final void setStep( final int step ) {
519         _step = step;
520     }
521
522     public final void setStepForDiagnostics( final int step_for_diagnostics ) {
523         _step_for_diagnostics = step_for_diagnostics;
524     }
525
526     final public String writeAndAlignRemovedSeqs() throws IOException, InterruptedException {
527         final StringBuilder msg = new StringBuilder();
528         final String n = _removed_seqs_out_base + "_" + _removed_seqs.size() + ".fasta";
529         SequenceWriter.writeSeqs( _removed_seqs, new File( n ), SEQ_FORMAT.FASTA, 100 );
530         msg.append( "wrote " + _removed_seqs.size() + " removed sequences to " + "\"" + n + "\"" );
531         if ( _realign ) {
532             final MsaInferrer mafft = Mafft.createInstance( _path_to_mafft );
533             final List<String> opts = new ArrayList<String>();
534             for( final String o : _maffts_opts.split( "\\s" ) ) {
535                 opts.add( o );
536             }
537             final Msa removed_msa = mafft.infer( _removed_seqs, opts );
538             final Double gr = MsaMethods.calcGapRatio( removed_msa );
539             String s = _removed_seqs_out_base + "_" + removed_msa.getNumberOfSequences() + "_"
540                     + removed_msa.getLength() + "_" + ForesterUtil.roundToInt( gr * 100 );
541             final String suffix = obtainSuffix();
542             s += suffix;
543             writeMsa( removed_msa, s, _output_format );
544             msg.append( ", and as MSA of length " + removed_msa.getLength() + " to \"" + s + "\"" );
545         }
546         return msg.toString();
547     }
548
549     final public String writeMsa( final File outfile ) throws IOException {
550         final Double gr = MsaMethods.calcGapRatio( _msa );
551         final String s = outfile + "_" + _msa.getNumberOfSequences() + "_" + _msa.getLength() + "_"
552                 + ForesterUtil.roundToInt( gr * 100 );
553         writeMsa( _msa, s + obtainSuffix(), _output_format );
554         return s;
555     }
556
557     final int calcNonGapResidues( final MolecularSequence seq ) {
558         int ng = 0;
559         for( int i = 0; i < seq.getLength(); ++i ) {
560             if ( !seq.isGapAt( i ) ) {
561                 ++ng;
562             }
563         }
564         return ng;
565     }
566
567     private final GapContribution[] calcGapContribtions( final boolean normalize_for_effective_seq_length ) {
568         final double gappiness[] = calcGappiness();
569         final GapContribution stats[] = new GapContribution[ _msa.getNumberOfSequences() ];
570         for( int row = 0; row < _msa.getNumberOfSequences(); ++row ) {
571             stats[ row ] = new GapContribution( _msa.getIdentifier( row ) );
572             for( int col = 0; col < _msa.getLength(); ++col ) {
573                 if ( !_msa.isGapAt( row, col ) ) {
574                     stats[ row ].addToValue( gappiness[ col ] );
575                 }
576             }
577             if ( normalize_for_effective_seq_length ) {
578                 stats[ row ].divideValue( calcNonGapResidues( _msa.getSequence( row ) ) );
579             }
580             else {
581                 stats[ row ].divideValue( _msa.getLength() );
582             }
583         }
584         return stats;
585     }
586
587     final private GapContribution[] calcGapContribtionsStats( final boolean normalize_for_effective_seq_length ) {
588         final GapContribution stats[] = calcGapContribtions( normalize_for_effective_seq_length );
589         Arrays.sort( stats );
590         return stats;
591     }
592
593     private final double[] calcGappiness() {
594         final int l = _msa.getLength();
595         final double gappiness[] = new double[ l ];
596         final int seqs = _msa.getNumberOfSequences();
597         for( int i = 0; i < l; ++i ) {
598             gappiness[ i ] = ( double ) MsaMethods.calcGapSumPerColumn( _msa, i ) / seqs;
599         }
600         return gappiness;
601     }
602
603     private final Phylogeny collapse( final Msa msa, final int threshold ) {
604         final BasicSymmetricalDistanceMatrix m = PairwiseDistanceCalculator.calcFractionalDissimilarities( msa );
605         //TODO
606         return null;
607     }
608
609     private final Phylogeny inferNJphylogeny( final PWD_DISTANCE_METHOD pwd_distance_method,
610                                               final Msa msa,
611                                               final boolean write_matrix,
612                                               final String matrix_name ) {
613         BasicSymmetricalDistanceMatrix m = null;
614         switch ( pwd_distance_method ) {
615             case KIMURA_DISTANCE:
616                 m = PairwiseDistanceCalculator.calcKimuraDistances( msa );
617                 break;
618             case POISSON_DISTANCE:
619                 m = PairwiseDistanceCalculator.calcPoissonDistances( msa );
620                 break;
621             case FRACTIONAL_DISSIMILARITY:
622                 m = PairwiseDistanceCalculator.calcFractionalDissimilarities( msa );
623                 break;
624             default:
625                 throw new IllegalArgumentException( "invalid pwd method" );
626         }
627         if ( write_matrix ) {
628             try {
629                 m.write( ForesterUtil.createBufferedWriter( matrix_name ) );
630             }
631             catch ( final IOException e ) {
632                 e.printStackTrace();
633             }
634         }
635         final NeighborJoiningF nj = NeighborJoiningF.createInstance( false, 5 );
636         final Phylogeny phy = nj.execute( m );
637         return phy;
638     }
639
640     private final boolean isPrintMsaStats( final int i ) {
641         return ( ( ( _step == 1 ) && ( _step_for_diagnostics == 1 ) ) || ( ( _step_for_diagnostics > 0 ) && ( ( ( i + 1 ) % _step_for_diagnostics ) == 0 ) ) );
642     }
643
644     private final boolean isPrintMsaStatsWriteOutfileAndRealign( final int i ) {
645         return ( ( ( _step == 1 ) && ( _step_for_diagnostics == 1 ) ) || ( ( _step > 0 ) && ( ( ( i + 1 ) % _step ) == 0 ) ) );
646     }
647
648     private final StringBuilder msaPropertiesAsSB( final MsaProperties msa_properties ) {
649         final StringBuilder sb = new StringBuilder();
650         sb.append( msa_properties.getNumberOfSequences() );
651         sb.append( "\t" );
652         sb.append( msa_properties.getLength() );
653         sb.append( "\t" );
654         sb.append( NF_4.format( msa_properties.getGapRatio() ) );
655         sb.append( "\t" );
656         sb.append( NF_1.format( msa_properties.getAvgNumberOfGaps() ) );
657         if ( _calculate_shannon_entropy ) {
658             sb.append( "\t" );
659             sb.append( NF_4.format( msa_properties.getEntropy7() ) );
660             sb.append( "\t" );
661             sb.append( NF_4.format( msa_properties.getEntropy21() ) );
662         }
663         return sb;
664     }
665
666     private String obtainSuffix() {
667         if ( _output_format == MSA_FORMAT.FASTA ) {
668             return ".fasta";
669         }
670         else if ( _output_format == MSA_FORMAT.PHYLIP ) {
671             return ".aln";
672         }
673         return "";
674     }
675
676     private final Phylogeny pi( final String matrix, final int boostrap ) {
677         final Phylogeny master_phy = inferNJphylogeny( PWD_DISTANCE_METHOD.KIMURA_DISTANCE, _msa, true, matrix );
678         final int seed = 15;
679         final int n = 100;
680         final ResampleableMsa resampleable_msa = new ResampleableMsa( _msa );
681         final int[][] resampled_column_positions = BootstrapResampler.createResampledColumnPositions( _msa.getLength(),
682                                                                                                       n,
683                                                                                                       seed );
684         final Phylogeny[] eval_phys = new Phylogeny[ n ];
685         for( int i = 0; i < n; ++i ) {
686             resampleable_msa.resample( resampled_column_positions[ i ] );
687             eval_phys[ i ] = inferNJphylogeny( PWD_DISTANCE_METHOD.KIMURA_DISTANCE, resampleable_msa, false, null );
688         }
689         ConfidenceAssessor.evaluate( "bootstrap", eval_phys, master_phy, true, 1 );
690         PhylogenyMethods.extractFastaInformation( master_phy );
691         return master_phy;
692     }
693
694     private final void printMsaProperties( final MsaProperties msa_properties ) {
695         if ( ( _step == 1 ) || ( _step_for_diagnostics == 1 ) ) {
696             System.out.print( ForesterUtil.pad( msa_properties.getRemovedSeq(), _longest_id_length, ' ', false ) );
697             System.out.print( "\t" );
698         }
699         System.out.print( msaPropertiesAsSB( msa_properties ) );
700         System.out.print( "\t" );
701     }
702
703     final private MsaProperties printMsaStatsWriteOutfileAndRealign( final boolean realign, final String id )
704             throws IOException, InterruptedException {
705         if ( realign ) {
706             realignWithMafft();
707         }
708         final MsaProperties msa_prop = new MsaProperties( _msa, id, _calculate_shannon_entropy );
709         printMsaProperties( msa_prop );
710         final String s = writeOutfile();
711         System.out.print( "-> " + s + ( realign ? "\t(realigned)" : "" ) );
712         return msa_prop;
713     }
714
715     private final void printTableHeader() {
716         if ( ( _step == 1 ) || ( _step_for_diagnostics == 1 ) ) {
717             System.out.print( ForesterUtil.pad( "Id", _longest_id_length, ' ', false ) );
718             System.out.print( "\t" );
719         }
720         System.out.print( "Seqs" );
721         System.out.print( "\t" );
722         System.out.print( "Length" );
723         System.out.print( "\t" );
724         System.out.print( "Gap R" );
725         System.out.print( "\t" );
726         System.out.print( "Gaps" );
727         System.out.print( "\t" );
728         if ( _calculate_shannon_entropy ) {
729             System.out.print( "entn7" );
730             System.out.print( "\t" );
731             System.out.print( "entn21" );
732             System.out.print( "\t" );
733         }
734         System.out.println();
735     }
736
737     final private void realignWithMafft() throws IOException, InterruptedException {
738         final MsaInferrer mafft = Mafft.createInstance( _path_to_mafft );
739         final List<String> opts = new ArrayList<String>();
740         for( final String o : _maffts_opts.split( "\\s" ) ) {
741             opts.add( o );
742         }
743         _msa = DeleteableMsa.createInstance( mafft.infer( _msa.asSequenceList(), opts ) );
744     }
745
746     final private void removeGapColumns() {
747         _msa.deleteGapOnlyColumns();
748     }
749
750     private final String writeOutfile() throws IOException {
751         final String s = writeMsa( _out_file_base );
752         return s;
753     }
754
755     // Returns null if not path found.
756     final public static String guessPathToMafft() {
757         String path;
758         if ( ForesterUtil.OS_NAME.toLowerCase().indexOf( "win" ) >= 0 ) {
759             path = "C:\\Program Files\\mafft-win\\mafft.bat";
760             if ( MsaInferrer.isInstalled( path ) ) {
761                 return path;
762             }
763         }
764         path = "/home/czmasek/SOFTWARE/MSA/MAFFT/mafft-7.130-without-extensions/scripts/mafft";
765         if ( MsaInferrer.isInstalled( path ) ) {
766             return path;
767         }
768         path = "/usr/local/bin/mafft";
769         if ( MsaInferrer.isInstalled( path ) ) {
770             return path;
771         }
772         path = "/usr/bin/mafft";
773         if ( MsaInferrer.isInstalled( path ) ) {
774             return path;
775         }
776         path = "/bin/mafft";
777         if ( MsaInferrer.isInstalled( path ) ) {
778             return path;
779         }
780         path = "mafft";
781         if ( MsaInferrer.isInstalled( path ) ) {
782             return path;
783         }
784         return null;
785     }
786
787     final private static void writeMsa( final Msa msa, final String outfile, final MSA_FORMAT format )
788             throws IOException {
789         final Writer w = ForesterUtil.createBufferedWriter( outfile );
790         msa.write( w, format );
791         w.close();
792     }
793 }