// This file is part of BULL, a program for phylogenetic simulations // most of the code was written by Mark T. Holder. // This program is for internal use by the lab of Dr. Tandy Warnow only. // Do not redistribute the code. It is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // // Some of the code is from publically available source by Paul Lewis, Ziheng Yang, // John Huelsenbeck, David Swofford , and others (as noted in the code). // In fact the main structure of the program was created by modifying Paul Lewis' // basiccmdline.cpp from his NCL // // This code was used in Mark's dissertation, some changes were made in order to // get it to compile on gcc. It is possible that this porting introduced bugs (very // little debugging has been done on UNIX platforms). I would suggest checking // the simulator by generating data on trees with short branches, etc. // This file is part of BULL, a program for phylogenetic simulations // most of the code was written by Mark T. Holder. // This program is for internal use by the lab of Dr. Tandy Warnow only. // Do not redistribute the code. It is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // // Some of the code is from publically available source by Paul Lewis, Ziheng Yang, // John Huelsenbeck, David Swofford , and others (as noted in the code). // In fact the main structure of the program was created by modifying Paul Lewis' // basiccmdline.cpp from his NCL // // This code was used in Mark's dissertation, some changes were made in order to // get it to compile on gcc. It is possible that this porting introduced bugs (very // little debugging has been done on UNIX platforms). I would suggest checking // the simulator by generating data on trees with short branches, etc. #include "nexus_defs.hpp" #include "xbull.hpp" #include "nexus_token.hpp" #include "nexus.hpp" #include "taxa_block.hpp" #include "trees_block.hpp" using namespace::std; /** * @class TreesBlock * @file taxa_block.hpp * @file taxablock.cpp * @author Paul O. Lewis * @copyright Copyright © 1999. All Rights Reserved. */ /** * @constructor * * Initializes id to "TREES" and ntrees and defaultTrees to 0. */ TreesBlock::TreesBlock( TaxaBlock& tb ) : ntrees(0), defaultTree(0), taxa(tb), NexusBlock() { id = "TREES"; } /** * @destructor * * Flushes translateList, rooted, and treeList. */ TreesBlock::~TreesBlock() { translateList.erase( translateList.begin(), translateList.end() ); rooted.erase( rooted.begin(), rooted.end() ); treeName.erase( treeName.begin(), treeName.end() ); treeDescription.erase( treeDescription.begin(), treeDescription.end() ); } /** * @method Read [void:protected] * @param token [NexusToken&] the token used to read from in * @throws XBull * * This function provides the ability to read everything following the block name * (which is read by the Nexus object) to the end or endblock statement. * Characters are read from the input stream in. Overrides the * abstract virtual function in the base class. */ void TreesBlock::Read( NexusToken& token ) { isEmpty = false; token.GetNextToken(); // this should be the semicolon after the block name if ( !token.Equals(";") ) { errormsg = "Expecting ';' after TREES block name, but found "; errormsg += token.GetToken(); errormsg += " instead"; throw XBull( errormsg, token); } for (;;) { token.GetNextToken(); if ( token.Equals("TRANSLATE") ) { int numEntries = taxa.GetNumTaxonLabels(); for ( int k = 0; k < numEntries; k++ ) { // create the Association // get the key token.GetNextToken(); std::string skey = token.GetToken(); // get the value token.GetNextToken(); std::string sval = token.GetToken(); // add the Association object to the translate list translateList[ skey ] = sval; // this should be a comma, unless we are at the last pair, in // which case it should be a semicolon token.GetNextToken(); if ( k < numEntries-1 ) { if ( !token.Equals(",") ) { errormsg = "Expecting ',' to terminate each key/value pair in TRANSLATE command, but found "; errormsg +=token.GetToken(); errormsg += " instead"; throw XBull( errormsg, token); } } else { if ( !token.Equals(";") ) { errormsg = "Expecting ';' to terminate the TRANSLATE command, but found "; errormsg +=token.GetToken(); errormsg += " instead"; throw XBull( errormsg, token); } } } } else if ( token.Equals("TREE") ) { // this should be either an asterisk or a tree name token.GetNextToken(); if ( token.Equals("*") ) { defaultTree = ntrees; // ntrees is not incremented until entire tree command has been read // this should be tree name token.GetNextToken(); } // save the tree name as the key std::string skey = token.GetToken(); // this should be an equals sign token.GetNextToken(); if ( !token.Equals("=") ) { errormsg = "Expecting '=' after tree name in TREE command, but found "; errormsg += token.GetToken(); errormsg += " instead"; throw XBull( errormsg, token); } // this should be either a tree description or a command comment specifying // whether this tree is to be rooted ([&R]) or unrooted ([&U]). token.SetLabileFlagBit( NexusToken::saveCommandComments ); token.SetLabileFlagBit( NexusToken::parentheticalToken ); token.GetNextToken(); std::string s = token.GetToken(); if ( s.size() < 2 ) { errormsg = "Expecting command comment or tree description in TREE command, but found "; errormsg += token.GetToken(); errormsg += " instead"; throw XBull( errormsg, token); } if ( s[0] == '&' ) { // command comment found if ( s[1] == 'R' || s[1] == 'r' ) rooted.push_back(true); else if ( s[1] == 'U' || s[1] == 'u' ) rooted.push_back(false); else { errormsg = "["; errormsg += token.GetToken(); errormsg += "] is not a valid command comment in a TREE command"; throw XBull( errormsg, token); } // this should be only the tree description token.SetLabileFlagBit( NexusToken::parentheticalToken ); token.GetNextToken(); } std::string sval = token.GetToken(); // this should be a semicolon token.GetNextToken(); if ( !token.Equals(";") ) { errormsg = "Expecting ';' to terminate the TREE command, but found "; errormsg += token.GetToken(); errormsg += " instead"; throw XBull( errormsg, token); } ntrees++; treeName.push_back( skey ); treeDescription.push_back( sval ); if ( rooted.size() < ntrees ) rooted.push_back(false); } else if ( token.Equals("END") ) { // get the semicolon following END token.GetNextToken(); if ( !token.Equals(";") ) { errormsg = "Expecting ';' to terminate the END command, but found "; errormsg += token.GetToken(); errormsg += " instead"; throw XBull( errormsg, token); } break; } else if ( token.Equals("ENDBLOCK") ) { // get the semicolon following ENDBLOCK token.GetNextToken(); if ( !token.Equals(";") ) { errormsg = "Expecting ';' to terminate the ENDBLOCK command, but found "; errormsg += token.GetToken(); errormsg += " instead"; throw XBull( errormsg, token); } break; } else { SkippingCommand( token.GetToken() ); do { token.GetNextToken(); } while ( !token.AtEOF() && !token.Equals(";") ); if ( token.AtEOF() ) { errormsg = "Unexpected end of file encountered"; throw XBull( errormsg, token); } } } } /** * @method Reset [void:protected] * * Flushes treeList, translateList and rooted, and sets ntrees to 0 * in preparation for reading a new TREES block. */ void TreesBlock::Reset() { isEmpty = true; treeName.erase( treeName.begin(), treeName.end() ); treeDescription.erase( treeDescription.begin(), treeDescription.end() ); translateList.erase( translateList.begin(), translateList.end() ); rooted.erase( rooted.begin(), rooted.end() ); ntrees = 0; } /** * @method GetNumDefaultTree [int:public] * * Returns the 0-offset index of the default tree, which will be 0 if there is * only one tree stored or no trees stored. If more than one tree is stored, * the default tree will be the one specifically indicated by the user * (using an asterisk in the data file) to be the default tree (or 0 if the * user failed to specify. */ int TreesBlock::GetNumDefaultTree() { return defaultTree; } /** * @method GetNumTrees [int:public] * * Returns the number of trees stored in this TreesBlock object. */ int TreesBlock::GetNumTrees() { return ntrees; } /** * @method GetTreeName [char*:public] * @param i [int] the index of the tree for which the name is to be returned * * Returns the name of the tree stored at position i in treeList. Assumes * that i will be in the range 0...ntrees-1. */ std::string TreesBlock::GetTreeName( int i ) { assert( i >= 0 ); assert( i < ntrees ); return treeName[i]; //trash return treeList.GetKey(i); } static std::string& blanks_to_underscores( std::string& s ) { int len = s.length(); for ( int k = 0; k < len; k++ ) { if ( s[k] == ' ' ) s[k] = '_'; } return s; } /** * @method GetTranslatedTreeDescription [std::string:public] * @param i [int] the index of the tree for which the description is to be returned * * Returns the description of the tree stored at position i in treeList. Assumes * that i will be in the range 0...ntrees-1. Node numbers will be translated to * names in the resulting tree description. Use GetTreeDescription if translation * is not desired. When translating, blank spaces in names are converted to * underscores. */ std::string TreesBlock::GetTranslatedTreeDescription( int i ) { assert( i >= 0 ); assert( i < ntrees ); std::string s = treeDescription[i]; std::string x; x += s[0]; int slen = s.size(); assert( slen > 1 ); for ( int k = 1; k < slen; k++ ) { char prev; if (isgraph(s[k-1])) prev = s[k-1]; char curr = s[k]; if ( isdigit(curr) && ( prev == '(' || prev == ',' ) ) { std::string ns; ns += curr; for (;;) { curr = s[k+1]; prev = s[k++]; if ( isdigit(curr) ) ns += curr; else { --k; break; } } std::string nss = translateList[ns]; x += blanks_to_underscores( nss ); } else x += curr; } return x; } /** * @method GetTreeDescription [std::string:public] * @param i [int] the index of the tree for which the description is to be returned * * Returns the description of the tree stored at position i in treeList. Assumes * that i will be in the range 0...ntrees-1. */ std::string TreesBlock::GetTreeDescription( int i ) { assert( i >= 0 ); assert( i < ntrees ); return treeDescription[i]; //trash return treeList.GetValue(i); } /** * @method IsDefaultTree [int:public] * @param i [int] the index of the tree in question * * Returns true if the ith tree (0-offset) is the default tree, 0 otherwise. * Assumes that i will be in the range 0...ntrees-1. */ int TreesBlock::IsDefaultTree( int i ) { assert( i >= 0 ); assert( i < ntrees ); if ( i == GetNumDefaultTree() ) return 1; else return 0; } /** * @method IsRootedTree [int:public] * @param i [int] the index of the tree in question * * Returns true if the ith tree (0-offset) is rooted, 0 otherwise. * Assumes that i will be in the range 0...ntrees-1. */ int TreesBlock::IsRootedTree( int i ) { assert( i >= 0 ); assert( i < ntrees ); return (int)rooted[i]; } /** * @method Report [void:public] * @param out [ostream&] the output stream to which to write the report * * This function outputs a brief report of the contents of this taxa block. * Overrides the abstract virtual function in the base class. */ void TreesBlock::Report(std::ostream& out ) { out << endl; out << id << " block contains "; if ( ntrees == 0 ) { out << "no trees" << endl; } else if ( ntrees == 1 ) out << "one tree" << endl; else out << ntrees << " trees" << endl; if ( ntrees == 0 ) return; for ( int k = 0; k < ntrees; k++ ) { out << '\t' << (k+1) << '\t' << treeName[k]; out << "\t("; if ( rooted[k] ) out << "rooted"; else out << "unrooted"; if ( defaultTree == k ) out << ",default tree)" << endl; else out << ')' << endl; } }