(C++) CheckNewick
January 7, 2018 · View on GitHub
(C++) CheckNewick
CheckNewick is a Newick code snippets to determine if a std::string is a well-formed Newick.
struct BinaryNewickVector { enum { bracket_open = -1 }; enum { bracket_close = -2 }; enum { comma = -3 }; enum { new_line = -4 }; enum { null = -5 }; }; ///CheckNewick checks if a std::string is a valid Newick. ///If this std::string is not a valid Newick, ///CheckNewick throws an exception with a detailed description ///From http://www.richelbilderbeek.nl/CppCheckNewick.htm void CheckNewick(const std::string& s) { if (s.size()<3) throw std::invalid_argument( "The Newick std::string must have a size of at least three characters"); if (s[0]!='(') throw std::invalid_argument( "The Newick std::string must start with an opening bracket ('(')."); if (s[s.size()-1]!=')') throw std::invalid_argument( "The Newick std::string must end with a closing bracket (')')."); if (std::count(s.begin(),s.end(),'(') !=std::count(s.begin(),s.end(),')')) throw std::invalid_argument( "The Newick std::string must have as much opening " "as closing brackets #1"); std::string s_copy = s; while(s_copy.size()>2) //Find a leaf and cut it until the string is empty { //Find a leaf //Find index i (starting opening bracket) and j (closing bracket) const std::size_t sz = s_copy.size(); std::size_t i = 0; std::size_t j = 0; for (i=0 ; i!=sz; ++i) //Index of opening bracket { if (s_copy[i]!='(') continue; if (s_copy[i+1]==')') throw std::invalid_argument( "The Newick std::string cannot have " "a consecutive opening and closing bracket"); for (j=i+1; j!=sz; ++j) { if (s_copy[j]=='(') { j = 0; break; } if (s_copy[j]!=')') continue; break; } if (j == 0) continue; //j cannot be 0 after previous for loop if (j == sz) throw std::invalid_argument( "The Newick std::string must have as much opening as closing brackets #2"); break; } if (s_copy[i]!='(') throw std::invalid_argument( "The Newick std::string must have as much opening as closing brackets #3"); //Indices i and j found //Is range between i and j valid? if (s_copy[i]!='(') throw std::logic_error( "Bilderbikkel incorrectly assumes that s_copy[i]=='('"); if (s_copy[j]!=')') throw std::logic_error( "Bilderbikkel incorrectly assumes that s_copy[j]==')'"); if (i + 1 != sz && s_copy[i+1]==',') throw std::invalid_argument( "After bracket open, there can be no comma"); if (j != 0 && s_copy[j-1]==',') throw std::invalid_argument( "Before bracket close, there can be no comma"); //Check the range for (size_t k=i+1; k!=j; ++k) { if ( s_copy[k]!='0' && s_copy[k]!='1' && s_copy[k]!='2' && s_copy[k]!='3' && s_copy[k]!='4' && s_copy[k]!='5' && s_copy[k]!='6' && s_copy[k]!='7' && s_copy[k]!='8' && s_copy[k]!='9' && s_copy[k]!='0' && s_copy[k]!=',') { std::stringstream err_msg; err_msg << "Invalid non-number character in input: '" << s_copy[k] << "'"; throw std::invalid_argument(err_msg.str().c_str()); } if (k + 1 != sz && s_copy[k] == ',' && s_copy[k+1] == ',') throw std::invalid_argument( "There can be no consecutive comma's"); } if (i > 0 && s_copy[i-1]=='(' & j +1 < sz && s_copy[j + 1] == ')') { throw std::invalid_argument( "Newicks must not have the form ((X))"); } //Range is assumed valid //Cut the leaf (turns '(1,2)' to (9) ) assert(s_copy[i]=='('); assert(s_copy[j]==')'); const std::string s_new_1 = s_copy.substr(0,i); const std::string s_new_2 = s_copy.substr(j+1,sz-j-1); //OK const std::string s_new = s_new_1 + "9" + s_new_2; s_copy = s_new; } } ///CheckNewick checks if a std::vector<int> is a valid Newick. ///If this std::vector<int> is not a valid Newick, ///CheckNewick throws an exception with a detailed description ///From http://www.richelbilderbeek.nl/CppCheckNewick.htm void CheckNewick(const std::vector<int>& v) { if (v.size()<3) throw std::invalid_argument( "The Newick std::vector<int> must have a size of at least three characters"); if (v[0]!=BinaryNewickVector::bracket_open) throw std::invalid_argument( "The Newick std::vector<int> must start with an opening bracket ('(')."); if (v[v.size()-1]!=BinaryNewickVector::bracket_close) throw std::invalid_argument( "The Newick std::vector<int> must end with a closing bracket (')')."); if (std::count(v.begin(),v.end(),static_cast<int>(BinaryNewickVector::bracket_open)) !=std::count(v.begin(),v.end(),static_cast<int>(BinaryNewickVector::bracket_close))) throw std::invalid_argument( "The Newick std::string must have as much opening " "as closing brackets #1"); std::vector<int> v_copy = v; while(v_copy.size()>2) //Find a leaf and cut it until the string is empty { //Find a leaf //Find index i (starting opening bracket) and j (closing bracket) const std::size_t sz = v_copy.size(); std::size_t i = 0; std::size_t j = 0; for (i=0 ; i!=sz; ++i) //Index of opening bracket { if (v_copy[i]!=BinaryNewickVector::bracket_open) continue; if (v_copy[i+1]==BinaryNewickVector::bracket_close) throw std::invalid_argument( "The Newick std::vector<int> cannot have " "a consecutive opening and closing bracket"); for (j=i+1; j!=sz; ++j) { if (v_copy[j]==BinaryNewickVector::bracket_open) { j = 0; break; } if (v_copy[j]!=BinaryNewickVector::bracket_close) continue; break; } if (j == 0) continue; //j cannot be 0 after previous for loop if (j == sz) throw std::invalid_argument( "The Newick std::vector<int> must have as much opening " "as closing brackets #2"); break; } if (v_copy[i]!=BinaryNewickVector::bracket_open) throw std::invalid_argument( "The Newick std::vector<int> must have as much opening " "as closing brackets #3"); //Indices i and j found //Is range between i and j valid? if (v_copy[i]!=BinaryNewickVector::bracket_open) throw std::logic_error( "Bilderbikkel incorrectly assumes that s_copy[i]=='('"); if (v_copy[j]!=BinaryNewickVector::bracket_close) throw std::logic_error( "Bilderbikkel incorrectly assumes that s_copy[j]==')'"); //Check the range for (size_t k=i+1; k!=j; ++k) { if (v_copy[k] < 0) { std::ostringstream err_msg; err_msg << "Invalid non-number in input: '" << v_copy[k] << "'"; throw std::invalid_argument(err_msg.str().c_str()); } } //Range is assumed valid //Cut the leaf //Changes '(1,2)' to '(999)' assert(v_copy[i]==BinaryNewickVector::bracket_open); assert(v_copy[j]==BinaryNewickVector::bracket_close); std::vector<int> v_new(v_copy.begin(),v_copy.begin() + i); v_new.push_back(999); std::copy(v_copy.begin() + j + 1, v_copy.end(),std::back_inserter(v_new)); v_copy = v_new; } }