// ----------------------------------------
// ---------- sequence.cc -----------
// ----------------------------------------
+#include <boost/filesystem/fstream.hpp>
+#include <boost/filesystem/operations.hpp>
+namespace fs = boost::filesystem;
+
+#include <boost/spirit/core.hpp>
+#include <boost/spirit/actor/push_back_actor.hpp>
+#include <boost/spirit/iterator/file_iterator.hpp>
+#include <boost/spirit/utility/chset.hpp>
+namespace spirit = boost::spirit;
#include "alg/sequence.hpp"
#include "mussa_exceptions.hpp"
#include <string>
+#include <stdexcept>
#include <iostream>
-
-using namespace std;
+#include <sstream>
+#include <set>
annot::annot()
- : start(0),
+ : begin(0),
end(0),
type(""),
name("")
{
}
-annot::annot(int start, int end, std::string type, std::string name)
- : start(start),
+annot::annot(int begin, int end, std::string type, std::string name)
+ : begin(begin),
end(end),
type(type),
name(name)
{
}
-motif::motif(int start, std::string motif)
- : annot(start, start+motif.size(), "motif", motif),
+annot::~annot()
+{
+}
+
+bool operator==(const annot& left, const annot& right)
+{
+ return ((left.begin== right.begin) and
+ (left.end == right.end) and
+ (left.type == right.type) and
+ (left.name == right.name));
+}
+
+motif::motif(int begin, std::string motif)
+ : annot(begin, begin+motif.size(), "motif", motif),
sequence(motif)
{
}
-
-Sequence::Sequence()
- : sequence(""),
+
+motif::~motif()
+{
+}
+
+
+Sequence::Sequence(alphabet_ref alphabet_)
+ : parent(0),
+ alphabet(alphabet_),
+ seq_start(0),
+ seq_count(0),
+ strand(UnknownStrand)
+{
+}
+
+Sequence::~Sequence()
+{
+}
+
+Sequence::Sequence(const char *seq, alphabet_ref alphabet_)
+ : parent(0),
+ alphabet(alphabet_),
+ seq_start(0),
+ seq_count(0),
+ strand(UnknownStrand),
header(""),
species("")
{
- annots.clear();
- motif_list.clear();
+ set_filtered_sequence(seq, alphabet);
}
-Sequence::Sequence(string seq)
+Sequence::Sequence(const std::string& seq, alphabet_ref alphabet_)
+ : parent(0),
+ alphabet(alphabet_),
+ seq_start(0),
+ seq_count(0),
+ strand(UnknownStrand),
+ header(""),
+ species("")
+{
+ set_filtered_sequence(seq, alphabet);
+}
+
+Sequence::Sequence(const Sequence& o)
+ : parent(o.parent),
+ seq(o.seq),
+ alphabet(o.alphabet),
+ seq_start(o.seq_start),
+ seq_count(o.seq_count),
+ strand(o.strand),
+ header(o.header),
+ species(o.species),
+ annots(o.annots),
+ motif_list(o.motif_list)
{
- set_filtered_sequence(seq);
}
Sequence &Sequence::operator=(const Sequence& s)
{
if (this != &s) {
- sequence = s.sequence;
+ parent = s.parent;
+ seq = s.seq;
+ alphabet = s.alphabet;
+ seq_start = s.seq_start;
+ seq_count = s.seq_count;
+ strand = s.strand;
header = s.header;
species = s.species;
annots = s.annots;
+ motif_list = s.motif_list;
}
return *this;
}
-Sequence &Sequence::operator=(const std::string& s)
+static void multiplatform_getline(std::istream& in, std::string& line)
{
- set_filtered_sequence(s);
- return *this;
+ line.clear();
+ char c;
+ in.get(c);
+ while(in.good() and !(c == '\012' or c == '\015') ) {
+ line.push_back(c);
+ in.get(c);
+ }
+ // if we have cr-lf eat it
+ c = in.peek();
+ if (c=='\012' or c == '\015') {
+ in.get();
+ }
}
-char Sequence::operator[](int index) const
+void Sequence::load_fasta(fs::path file_path, int seq_num, int start_index, int end_index)
{
- return sequence[index];
+ load_fasta(file_path, alphabet, seq_num, start_index, end_index);
}
-ostream& operator<<(ostream& out, const Sequence& seq)
+//! load a fasta file into a sequence
+void Sequence::load_fasta(fs::path file_path, alphabet_ref a,
+ int seq_num, int start_index, int end_index)
{
- out << "Sequence(" << seq.get_seq() << ")";
- return out;
+ fs::fstream data_file;
+ data_file.open(file_path, std::ios::in);
+
+ if (!data_file.good())
+ {
+ throw mussa_load_error("Sequence File: "+file_path.string()+" not found");
+ } else {
+ try {
+ load_fasta(data_file, a, seq_num, start_index, end_index);
+ } catch(sequence_empty_error e) {
+ // there doesn't appear to be any sequence
+ // catch and rethrow to include the filename
+ std::stringstream msg;
+ msg << "The selected sequence in "
+ << file_path.native_file_string()
+ << " appears to be empty";
+ throw sequence_empty_error(msg.str());
+ } catch(sequence_empty_file_error e) {
+ std::stringstream errormsg;
+ errormsg << file_path.native_file_string()
+ << " did not have any fasta sequences" << std::endl;
+ throw sequence_empty_file_error(errormsg.str());
+ } catch(sequence_invalid_load_error e) {
+ std::ostringstream msg;
+ msg << file_path.native_file_string();
+ msg << " " << e.what();
+ throw sequence_invalid_load_error(msg.str());
+ }
+ }
+}
+
+void Sequence::load_fasta(std::istream& file,
+ int seq_num, int start_index, int end_index)
+{
+ load_fasta(file, alphabet, seq_num, start_index, end_index);
}
-//! load a fasta file into a sequence
-/*!
- * \param file_path the location of the fasta file in the filesystem
- * \param seq_num which sequence in the file to load
- * \param start_index starting position in the fasta sequence, 0 for beginning
- * \param end_index ending position in the fasta sequence, 0 for end
- * \return error message, empty string if no error. (gag!)
- */
void
-Sequence::load_fasta(string file_path, int seq_num,
+Sequence::load_fasta(std::istream& data_file, alphabet_ref a,
+ int seq_num,
int start_index, int end_index)
{
- fstream data_file;
- string file_data_line;
+ std::string file_data_line;
int header_counter = 0;
+ size_t line_counter = 0;
bool read_seq = true;
- string rev_comp;
- string sequence_raw;
- string seq_tmp; // holds sequence during basic filtering
-
- data_file.open(file_path.c_str(), ios::in);
+ std::string rev_comp;
+ std::string sequence_raw;
+ std::string seq_tmp; // holds sequence during basic filtering
+ const Alphabet &alpha = get_alphabet(a);
if (seq_num == 0) {
throw mussa_load_error("fasta sequence number is 1 based (can't be 0)");
}
- if (!data_file)
- {
- throw mussa_load_error("Sequence File: " + file_path + " not found");
- }
- // if file opened okay, read it
- else
+
+ // search for the header of the fasta sequence we want
+ while ( (!data_file.eof()) && (header_counter < seq_num) )
{
- // search for the header of the fasta sequence we want
- while ( (!data_file.eof()) && (header_counter < seq_num) )
- {
- getline(data_file,file_data_line);
- if (file_data_line.substr(0,1) == ">")
- header_counter++;
- }
+ multiplatform_getline(data_file, file_data_line);
+ ++line_counter;
+ if (file_data_line.substr(0,1) == ">")
+ header_counter++;
+ }
+ if (header_counter > 0) {
header = file_data_line.substr(1);
sequence_raw = "";
- while ( !data_file.eof() && read_seq )
- {
- getline(data_file,file_data_line);
+ while ( !data_file.eof() && read_seq ) {
+ multiplatform_getline(data_file,file_data_line);
+ ++line_counter;
if (file_data_line.substr(0,1) == ">")
read_seq = false;
- else sequence_raw += file_data_line;
+ else {
+ for (std::string::const_iterator line_i = file_data_line.begin();
+ line_i != file_data_line.end();
+ ++line_i)
+ {
+ if(alpha.exists(*line_i)) {
+ sequence_raw += *line_i;
+ } else {
+ std::ostringstream msg;
+ msg << "Unrecognized characters in fasta sequence at line ";
+ msg << line_counter;
+ throw sequence_invalid_load_error(msg.str());
+ }
+ }
+ }
}
- data_file.close();
-
// Lastly, if subselection of the sequence was specified we keep cut out
// and only keep that part
// end_index = 0 means no end was specified, so cut to the end
end_index = sequence_raw.size();
// sequence filtering for upcasing agctn and convert non AGCTN to N
- set_filtered_sequence(sequence_raw, start_index, end_index-start_index);
+ if (end_index-start_index <= 0) {
+ std::string msg("The selected sequence appears to be empty");
+ throw sequence_empty_error(msg);
+ }
+ set_filtered_sequence(sequence_raw, a, start_index, end_index-start_index);
+ } else {
+ std::string errormsg("There were no fasta sequences");
+ throw sequence_empty_file_error(errormsg);
}
}
-void Sequence::set_filtered_sequence(const string &old_seq,
- string::size_type start,
- string::size_type count)
+void Sequence::set_filtered_sequence(const std::string &in_seq,
+ alphabet_ref alphabet_,
+ size_type start,
+ size_type count,
+ strand_type strand_)
{
- char conversionTable[257];
-
- if ( count == 0)
- count = old_seq.size() - start;
- sequence.clear();
- sequence.reserve(count);
-
- // Make a conversion table
-
- // everything we don't specify below will become 'N'
- for(int table_i=0; table_i < 256; table_i++)
- {
- conversionTable[table_i] = 'N';
- }
- // add end of string character for printing out table for testing purposes
- conversionTable[256] = '\0';
-
- // we want these to map to themselves - ie not to change
- conversionTable[(int)'A'] = 'A';
- conversionTable[(int)'T'] = 'T';
- conversionTable[(int)'G'] = 'G';
- conversionTable[(int)'C'] = 'C';
- // this is to upcase
- conversionTable[(int)'a'] = 'A';
- conversionTable[(int)'t'] = 'T';
- conversionTable[(int)'g'] = 'G';
- conversionTable[(int)'c'] = 'C';
+ alphabet = alphabet_;
+ if ( count == npos)
+ count = in_seq.size() - start;
+ boost::shared_ptr<seq_string> new_seq(new seq_string);
+ new_seq->reserve(count);
// finally, the actual conversion loop
- for(string::size_type seq_index = 0; seq_index < count; seq_index++)
+ const Alphabet& alpha_impl = get_alphabet(); // go get one of our actual alphabets
+ std::string::const_iterator seq_i = in_seq.begin()+start;
+ for(size_type i = 0; i != count; ++i, ++seq_i)
{
- sequence += conversionTable[ (int)old_seq[seq_index+start]];
+ if (alpha_impl.exists(*seq_i)) {
+ new_seq->append(1, toupper(*seq_i));
+ } else {
+ new_seq->append(1, 'N');
+ }
}
+ parent = 0;
+ seq = new_seq;
+ seq_start = 0;
+ seq_count = count;
+ strand = strand_;
}
- // this doesn't work properly under gcc 3.x ... it can't recognize toupper
- //transform(sequence.begin(), sequence.end(), sequence.begin(), toupper);
-
-
void
-Sequence::load_annot(string file_path, int start_index, int end_index)
+Sequence::load_annot(fs::path file_path, int start_index, int end_index)
{
- fstream data_file;
- string file_data_line;
- annot an_annot;
- string::size_type space_split_i;
- string annot_value;
- list<annot>::iterator list_i;
- string err_msg;
-
+ if (not fs::exists(file_path)) {
+ throw mussa_load_error("Annotation File " + file_path.string() + " was not found");
+ }
+ if (fs::is_directory(file_path)) {
+ throw mussa_load_error(file_path.string() +
+ " is a directory, please provide a file for annotations."
+ );
+ }
+ fs::fstream data_stream(file_path, std::ios::in);
+ if (!data_stream)
+ {
+ throw mussa_load_error("Error loading annotation file " + file_path.string());
+ }
- annots.clear();
- data_file.open(file_path.c_str(), ios::in);
+ // so i should probably be passing the parse function some iterators
+ // but the annotations files are (currently) small, so i think i can
+ // get away with loading the whole file into memory
+ std::string data;
+ char c;
+ while(data_stream.good()) {
+ data_stream.get(c);
+ data.push_back(c);
+ }
+ data_stream.close();
+
+ try {
+ parse_annot(data, start_index, end_index);
+ } catch(annotation_load_error e) {
+ std::ostringstream msg;
+ msg << file_path.native_file_string()
+ << " "
+ << e.what();
+ throw annotation_load_error(msg.str());
+ }
+}
- if (!data_file)
+/* If this works, yikes, this is some brain hurting code.
+ *
+ * what's going on is that when pb_annot is instantiated it stores references
+ * to begin, end, name, type, declared in the parse function, then
+ * when operator() is called it grabs values from those references
+ * and uses that to instantiate an annot object and append that to our
+ * annotation list.
+ *
+ * This weirdness is because the spirit library requires that actions
+ * conform to a specific prototype operator()(IteratorT, IteratorT)
+ * which doesn't provide any useful opportunity for me to actually
+ * grab the results of our parsing.
+ *
+ * so I instantiate this structure in order to have a place to grab
+ * my data from.
+ */
+
+struct push_back_annot {
+ std::list<annot>& annot_list;
+ int& begin;
+ int& end;
+ std::string& name;
+ std::string& type;
+ int &parsed;
+
+ push_back_annot(std::list<annot>& annot_list_,
+ int& begin_,
+ int& end_,
+ std::string& name_,
+ std::string& type_,
+ int &parsed_)
+ : annot_list(annot_list_),
+ begin(begin_),
+ end(end_),
+ name(name_),
+ type(type_),
+ parsed(parsed_)
{
- throw mussa_load_error("Sequence File: " + file_path + " not found");
}
- // if file opened okay, read it
- else
+
+ void operator()(std::string::const_iterator,
+ std::string::const_iterator) const
{
- getline(data_file,file_data_line);
- species = file_data_line;
+ //std::cout << "adding annot: " << begin << "|" << end << "|" << name << "|" << type << std::endl;
+ annot_list.push_back(annot(begin, end, name, type));
+ ++parsed;
+ };
+};
+
+struct push_back_seq {
+ std::list<Sequence>& seq_list;
+ std::string& name;
+ std::string& seq;
+ int &parsed;
+
+ push_back_seq(std::list<Sequence>& seq_list_,
+ std::string& name_,
+ std::string& seq_,
+ int &parsed_)
+ : seq_list(seq_list_),
+ name(name_),
+ seq(seq_),
+ parsed(parsed_)
+ {
+ }
- // end_index = 0 means no end was specified, so cut to the end
- if (end_index == 0)
- end_index = sequence.length();
+ void operator()(std::string::const_iterator,
+ std::string::const_iterator) const
+ {
+ // filter out newlines from our sequence
+ std::string new_seq;
+ for(std::string::const_iterator seq_i = seq.begin();
+ seq_i != seq.end();
+ ++seq_i)
+ {
+ if (*seq_i != '\015' && *seq_i != '\012') new_seq += *seq_i;
+ }
+ //std::cout << "adding seq: " << name << " " << new_seq << std::endl;
+
+ Sequence s(new_seq);
+ s.set_fasta_header(name);
+ seq_list.push_back(s);
+ ++parsed;
+ };
+};
- //cout << "START: " << start_index << " END: " << end_index << endl;
+void
+Sequence::parse_annot(std::string data, int start_index, int end_index)
+{
+ int start=0;
+ int end=0;
+ std::string name;
+ std::string type;
+ std::string seq;
+ std::list<annot> parsed_annots;
+ std::list<Sequence> query_seqs;
+ int parsed=0;
+
+ bool ok = spirit::parse(data.begin(), data.end(),
+ (
+ //begin grammar
+ !(
+ (
+ spirit::alpha_p >>
+ +(spirit::graph_p)
+ )[spirit::assign_a(species)] >>
+ +(spirit::space_p)
+ ) >>
+ *(
+ ( // ignore html tags
+ *(spirit::space_p) >>
+ spirit::ch_p('<') >>
+ +(~spirit::ch_p('>')) >>
+ spirit::ch_p('>') >>
+ *(spirit::space_p)
+ )
+ |
+ ( // parse an absolute location name
+ (spirit::uint_p[spirit::assign_a(start)] >>
+ +spirit::space_p >>
+ spirit::uint_p[spirit::assign_a(end)] >>
+ +spirit::space_p >>
+ (
+ spirit::alpha_p >>
+ *spirit::graph_p
+ )[spirit::assign_a(name)] >>
+ // optional type
+ !(
+ +spirit::space_p >>
+ (
+ spirit::alpha_p >>
+ *spirit::graph_p
+ )[spirit::assign_a(type)]
+ )
+ // to understand how this group gets set
+ // read the comment above struct push_back_annot
+ )[push_back_annot(parsed_annots, start, end, type, name, parsed)]
+ |
+ ((spirit::ch_p('>')|spirit::str_p(">")) >>
+ (*(spirit::print_p))[spirit::assign_a(name)] >>
+ spirit::eol_p >>
+ (+(spirit::chset<>(Alphabet::nucleic_cstr)))[spirit::assign_a(seq)]
+ )[push_back_seq(query_seqs, name, seq, parsed)]
+ ) >>
+ *spirit::space_p
+ )
+ //end grammar
+ )).full;
+ if (not ok) {
+ std::stringstream msg;
+ msg << "Error parsing annotation #" << parsed;
+ throw annotation_load_error(msg.str());
+ }
+ // add newly parsed annotations to our sequence
+ std::copy(parsed_annots.begin(), parsed_annots.end(), std::back_inserter(annots));
+ // go seearch for query sequences
+ find_sequences(query_seqs.begin(), query_seqs.end());
+}
- while ( !data_file.eof() )
- {
- getline(data_file,file_data_line);
- if (file_data_line != "")
- {
- // need to get 4 values...almost same code 4 times...
- // get annot start index
- space_split_i = file_data_line.find(" ");
- annot_value = file_data_line.substr(0,space_split_i);
- an_annot.start = atoi (annot_value.c_str());
- file_data_line = file_data_line.substr(space_split_i+1);
- // get annot end index
- space_split_i = file_data_line.find(" ");
- annot_value = file_data_line.substr(0,space_split_i);
- an_annot.end = atoi (annot_value.c_str());
- file_data_line = file_data_line.substr(space_split_i+1);
+void Sequence::add_annotation(const annot& a)
+{
+ annots.push_back(a);
+}
- //cout << "seq, annots: " << an_annot.start << ", " << an_annot.end
- // << endl;
+const std::list<annot>& Sequence::annotations() const
+{
+ return annots;
+}
- // get annot name
- space_split_i = file_data_line.find(" ");
- if (space_split_i == string::npos) // no entries for name & type
- {
- cout << "seq, annots - no name or type\n";
- an_annot.name = "";
- an_annot.type = "";
- }
- else
- {
- annot_value = file_data_line.substr(0,space_split_i);
- an_annot.name = annot_value;
- file_data_line = file_data_line.substr(space_split_i+1);
- // get annot type
- space_split_i = file_data_line.find(" ");
- if (space_split_i == string::npos) // no entry for type
- an_annot.type = "";
- else
- {
- annot_value = file_data_line.substr(0,space_split_i);
- an_annot.type = annot_value;
- }
- }
+Sequence
+Sequence::subseq(int start, int count)
+{
+ if (!seq) {
+ Sequence new_seq;
+ return new_seq;
+ }
+ // there might be an off by one error with start+count > size()
+ if ( count == npos || start+count > size()) {
+ count = size()-start;
+ }
+ Sequence new_seq(*this);
+ new_seq.parent = this;
+ new_seq.seq_start = seq_start+start;
+ new_seq.seq_count = count;
+
+ new_seq.motif_list = motif_list;
+ new_seq.annots.clear();
+ // attempt to copy & reannotate position based annotations
+ int end = start+count;
+
+ for(std::list<annot>::const_iterator annot_i = annots.begin();
+ annot_i != annots.end();
+ ++annot_i)
+ {
+ int annot_begin= annot_i->begin;
+ int annot_end = annot_i->end;
+
+ if (annot_begin < end) {
+ if (annot_begin >= start) {
+ annot_begin -= start;
+ } else {
+ annot_begin = 0;
+ }
- // add annot to list if it falls within the range of sequence specified
- if ((start_index <= an_annot.start) && (end_index >= an_annot.end))
- {
- an_annot.start -= start_index;
- an_annot.end -= start_index;
- annots.push_back(an_annot);
- }
- else
- cout << "FAILED!!!!!!\n";
+ if (annot_end < end) {
+ annot_end -= start;
+ } else {
+ annot_end = count;
}
- }
- data_file.close();
- /*
- // debugging check
- for(list_i = annots.begin(); list_i != annots.end(); ++list_i)
- {
- cout << (*list_i).start << "," << (*list_i).end << "\t";
- cout << (*list_i).name << "\t" << (*list_i).type << endl;
+ annot new_annot(annot_begin, annot_end, annot_i->type, annot_i->name);
+ new_seq.annots.push_back(new_annot);
}
- */
}
+
+ return new_seq;
}
-bool Sequence::empty() const
+std::string Sequence::create_reverse_map() const
{
- return (size() == 0);
+ std::string rc_map(256, '~');
+ // if we're rna, use U instead of T
+ // we might want to add an "is_rna" to sequence at somepoint
+ char TU = (alphabet == reduced_rna_alphabet) ? 'U' : 'T';
+ char tu = (alphabet == reduced_rna_alphabet) ? 'u' : 't';
+ rc_map['A'] = TU ; rc_map['a'] = tu ;
+ rc_map['T'] = 'A'; rc_map['t'] = 'a';
+ rc_map['U'] = 'A'; rc_map['u'] = 'a';
+ rc_map['G'] = 'C'; rc_map['g'] = 'c';
+ rc_map['C'] = 'G'; rc_map['c'] = 'g';
+ rc_map['M'] = 'K'; rc_map['m'] = 'k';
+ rc_map['R'] = 'Y'; rc_map['r'] = 'y';
+ rc_map['W'] = 'W'; rc_map['w'] = 'w';
+ rc_map['S'] = 'S'; rc_map['s'] = 's';
+ rc_map['Y'] = 'R'; rc_map['y'] = 'r';
+ rc_map['K'] = 'M'; rc_map['k'] = 'm';
+ rc_map['V'] = 'B'; rc_map['v'] = 'b';
+ rc_map['H'] = 'D'; rc_map['h'] = 'd';
+ rc_map['D'] = 'H'; rc_map['d'] = 'h';
+ rc_map['B'] = 'V'; rc_map['b'] = 'v';
+ rc_map['N'] = 'N'; rc_map['n'] = 'n';
+ rc_map['X'] = 'X'; rc_map['x'] = 'x';
+ rc_map['?'] = '?';
+ rc_map['.'] = '.';
+ rc_map['-'] = '-';
+ rc_map['~'] = '~'; // not really needed, but perhaps it's clearer.
+ return rc_map;
}
-const std::list<annot>& Sequence::annotations() const
+Sequence Sequence::rev_comp() const
{
- return annots;
+ std::string rev_comp;
+ rev_comp.reserve(length());
+
+ std::string rc_map = create_reverse_map();
+
+ // reverse and convert
+ Sequence::const_reverse_iterator seq_i;
+ Sequence::const_reverse_iterator seq_end = rend();
+ for(seq_i = rbegin();
+ seq_i != seq_end;
+ ++seq_i)
+ {
+ rev_comp.append(1, rc_map[*seq_i]);
+ }
+ return Sequence(rev_comp, alphabet);
}
-string::size_type Sequence::length() const
+void Sequence::set_fasta_header(std::string header_)
{
- return size();
+ header = header_;
+}
+
+void Sequence::set_species(const std::string& name)
+{
+ species = name;
}
-string::size_type Sequence::size() const
+std::string Sequence::get_species() const
{
- return sequence.size();
+ return species;
}
-Sequence::iterator Sequence::begin()
+
+std::string
+Sequence::get_fasta_header() const
{
- return sequence.begin();
+ return header;
}
-Sequence::const_iterator Sequence::begin() const
+std::string
+Sequence::get_name() const
{
- return sequence.begin();
+ if (header.size() > 0)
+ return header;
+ else if (species.size() > 0)
+ return species;
+ else
+ return "";
}
-Sequence::iterator Sequence::end()
+const Alphabet& Sequence::get_alphabet() const
{
- return sequence.end();
+ return get_alphabet(alphabet);
}
-Sequence::const_iterator Sequence::end() const
+const Alphabet& Sequence::get_alphabet(alphabet_ref alpha) const
{
- return sequence.end();
+ switch (alpha) {
+ case reduced_dna_alphabet:
+ return Alphabet::reduced_dna_alphabet();
+ case reduced_rna_alphabet:
+ return Alphabet::reduced_rna_alphabet();
+ case reduced_nucleic_alphabet:
+ return Alphabet::reduced_nucleic_alphabet();
+ case nucleic_alphabet:
+ return Alphabet::nucleic_alphabet();
+ case protein_alphabet:
+ return Alphabet::protein_alphabet();
+ default:
+ throw std::runtime_error("unrecognized alphabet type");
+ break;
+ }
}
+void Sequence::set_sequence(const std::string& s, alphabet_ref a)
+{
+ set_filtered_sequence(s, a);
+}
-const string&
-Sequence::get_seq() const
+std::string Sequence::get_sequence() const
{
- return sequence;
+ if (seq)
+ return *seq;
+ else
+ return std::string();
}
+Sequence::const_reference Sequence::operator[](Sequence::size_type i) const
+{
+ return at(i);
+}
-string
-Sequence::subseq(int start, int end) const
+Sequence::const_reference Sequence::at(Sequence::size_type i) const
{
- return sequence.substr(start, end);
+ if (!seq) throw std::out_of_range("empty sequence");
+ return seq->at(i+seq_start);
}
+void
+Sequence::clear()
+{
+ parent = 0;
+ seq.reset();
+ seq_start = 0;
+ seq_count = 0;
+ strand = UnknownStrand;
+ header.clear();
+ species.clear();
+ annots.clear();
+ motif_list.clear();
+}
-const char *
-Sequence::c_seq() const
+const char *Sequence::c_str() const
{
- return sequence.c_str();
+ if (seq)
+ return seq->c_str()+seq_start;
+ else
+ return 0;
}
-string
-Sequence::rev_comp() const
+Sequence::const_iterator Sequence::begin() const
{
- string rev_comp;
- char conversionTable[257];
- int seq_i, table_i, len;
+ if (seq and seq_count != 0)
+ return seq->begin()+seq_start;
+ else
+ return Sequence::const_iterator(0);
+}
- len = sequence.length();
- rev_comp.reserve(len);
- // make a conversion table
- // init all parts of conversion table to '~' character
- // '~' I doubt will ever appear in a sequence file (jeez, I hope)
- // and may the fleas of 1000 camels infest the genitals of any biologist (and
- // seven generations of their progeny) who decides to make it mean
- // something special!!!
- // PS - double the curse for any smartass non-biologist who tries it as well
- for(table_i=0; table_i < 256; table_i++)
- {
- conversionTable[table_i] = '~';
+Sequence::const_iterator Sequence::end() const
+{
+ if (seq and seq_count != 0) {
+ return seq->begin() + seq_start + seq_count;
+ } else {
+ return Sequence::const_iterator(0);
}
- // add end of string character for printing out table for testing purposes
- conversionTable[256] = '\0';
+}
- // add in the characters for the bases we want to convert
- conversionTable[(int)'A'] = 'T';
- conversionTable[(int)'T'] = 'A';
- conversionTable[(int)'G'] = 'C';
- conversionTable[(int)'C'] = 'G';
- conversionTable[(int)'N'] = 'N';
+Sequence::const_reverse_iterator Sequence::rbegin() const
+{
+ if (seq and seq_count != 0)
+ return seq->rbegin()+(seq->size()-(seq_start+seq_count));
+ else
+ return Sequence::const_reverse_iterator();
+}
- // finally, the actual conversion loop
- for(seq_i = len - 1; seq_i >= 0; seq_i--)
- {
- table_i = (int) sequence[seq_i];
- rev_comp += conversionTable[table_i];
+Sequence::const_reverse_iterator Sequence::rend() const
+{
+ if (seq and seq_count != 0) {
+ return rbegin() + seq_count;
+ } else {
+ return Sequence::const_reverse_iterator();
}
-
- return rev_comp;
}
+bool Sequence::empty() const
+{
+ return (seq_count == 0) ? true : false;
+}
-const string&
-Sequence::get_header() const
+Sequence::size_type Sequence::find_first_not_of(
+ const std::string& query,
+ Sequence::size_type index)
{
- return header;
+ typedef std::set<std::string::value_type> sequence_set;
+ sequence_set match_set;
+
+ for(const_iterator query_item = query.begin();
+ query_item != query.end();
+ ++query_item)
+ {
+ match_set.insert(*query_item);
+ }
+ for(const_iterator base = begin();
+ base != end();
+ ++base)
+ {
+ if(match_set.find(*base) == match_set.end()) {
+ return base-begin();
+ }
+ }
+ return Sequence::npos;
}
-/*
-//FIXME: i don't think this code is callable
-string
-Sequence::sp_name() const
+
+Sequence::size_type Sequence::start() const
{
- return species;
+ if (parent)
+ return seq_start - parent->start();
+ else
+ return seq_start;
}
-*/
-void
-Sequence::set_seq(const string& a_seq)
+Sequence::size_type Sequence::stop() const
{
- set_filtered_sequence(a_seq);
+ return start() + seq_count;
}
-
-/*
-string
-Sequence::species()
+Sequence::size_type Sequence::size() const
{
- return species;
+ return seq_count;
}
-*/
-void
-Sequence::clear()
+Sequence::size_type Sequence::length() const
{
- sequence = "";
- header = "";
- species = "";
- annots.clear();
+ return size();
}
void
-Sequence::save(fstream &save_file)
- //string save_file_path)
+Sequence::save(fs::fstream &save_file)
{
//fstream save_file;
- list<annot>::iterator annots_i;
+ std::list<annot>::iterator annots_i;
// not sure why, or if i'm doing something wrong, but can't seem to pass
// file pointers down to this method from the mussa control class
// so each call to save a sequence appends to the file started by mussa_class
- //save_file.open(save_file_path.c_str(), ios::app);
+ //save_file.open(save_file_path.c_str(), std::ios::app);
- save_file << "<Sequence>" << endl;
- save_file << sequence << endl;
- save_file << "</Sequence>" << endl;
+ save_file << "<Sequence>" << std::endl;
+ save_file << *this << std::endl;
+ save_file << "</Sequence>" << std::endl;
- save_file << "<Annotations>" << endl;
- save_file << species << endl;
+ save_file << "<Annotations>" << std::endl;
+ save_file << species << std::endl;
for (annots_i = annots.begin(); annots_i != annots.end(); ++annots_i)
{
- save_file << annots_i->start << " " << annots_i->end << " " ;
- save_file << annots_i->name << " " << annots_i->type << endl;
+ save_file << annots_i->begin << " " << annots_i->end << " " ;
+ save_file << annots_i->name << " " << annots_i->type << std::endl;
}
- save_file << "</Annotations>" << endl;
+ save_file << "</Annotations>" << std::endl;
//save_file.close();
}
void
-Sequence::load_museq(string load_file_path, int seq_num)
+Sequence::load_museq(fs::path load_file_path, int seq_num)
{
- fstream load_file;
- string file_data_line;
+ fs::fstream load_file;
+ std::string file_data_line;
int seq_counter;
annot an_annot;
- string::size_type space_split_i;
- string annot_value;
+ std::string::size_type space_split_i;
+ std::string annot_value;
annots.clear();
- load_file.open(load_file_path.c_str(), ios::in);
+ load_file.open(load_file_path, std::ios::in);
seq_counter = 0;
// search for the seq_num-th sequence
seq_counter++;
}
getline(load_file, file_data_line);
- sequence = file_data_line;
+ // looks like the sequence is written as a single line
+ set_filtered_sequence(file_data_line, reduced_dna_alphabet);
getline(load_file, file_data_line);
getline(load_file, file_data_line);
if (file_data_line == "<Annotations>")
// get annot start index
space_split_i = file_data_line.find(" ");
annot_value = file_data_line.substr(0,space_split_i);
- an_annot.start = atoi (annot_value.c_str());
+ an_annot.begin = atoi (annot_value.c_str());
file_data_line = file_data_line.substr(space_split_i+1);
// get annot end index
space_split_i = file_data_line.find(" ");
annot_value = file_data_line.substr(0,space_split_i);
an_annot.end = atoi (annot_value.c_str());
- if (space_split_i == string::npos) // no entry for type or name
+ if (space_split_i == std::string::npos) // no entry for type or name
{
- cout << "seq, annots - no type or name\n";
+ std::cout << "seq, annots - no type or name\n";
an_annot.type = "";
an_annot.name = "";
}
space_split_i = file_data_line.find(" ");
annot_value = file_data_line.substr(0,space_split_i);
an_annot.type = annot_value;
- if (space_split_i == string::npos) // no entry for name
+ if (space_split_i == std::string::npos) // no entry for name
{
- cout << "seq, annots - no name\n";
+ std::cout << "seq, annots - no name\n";
an_annot.name = "";
}
else // get annot name
}
annots.push_back(an_annot); // don't forget to actually add the annot
}
- //cout << "seq, annots: " << an_annot.start << ", " << an_annot.end
- // << "-->" << an_annot.type << "::" << an_annot.name << endl;
+ //std::cout << "seq, annots: " << an_annot.start << ", " << an_annot.end
+ // << "-->" << an_annot.type << "::" << an_annot.name << std::endl;
}
}
load_file.close();
}
-string
-Sequence::rc_motif(string a_motif)
-{
- string rev_comp;
- char conversionTable[257];
- int seq_i, table_i, len;
-
- len = a_motif.length();
- rev_comp.reserve(len);
-
- for(table_i=0; table_i < 256; table_i++)
- {
- conversionTable[table_i] = '~';
- }
- // add end of string character for printing out table for testing purposes
- conversionTable[256] = '\0';
-
- // add in the characters for the bases we want to convert (IUPAC)
- conversionTable[(int)'A'] = 'T';
- conversionTable[(int)'T'] = 'A';
- conversionTable[(int)'G'] = 'C';
- conversionTable[(int)'C'] = 'G';
- conversionTable[(int)'N'] = 'N';
- conversionTable[(int)'M'] = 'K';
- conversionTable[(int)'R'] = 'Y';
- conversionTable[(int)'W'] = 'W';
- conversionTable[(int)'S'] = 'S';
- conversionTable[(int)'Y'] = 'R';
- conversionTable[(int)'K'] = 'M';
- conversionTable[(int)'V'] = 'B';
- conversionTable[(int)'H'] = 'D';
- conversionTable[(int)'D'] = 'H';
- conversionTable[(int)'B'] = 'V';
-
- // finally, the actual conversion loop
- for(seq_i = len - 1; seq_i >= 0; seq_i--)
- {
- //cout << "** i = " << seq_i << " bp = " <<
- table_i = (int) a_motif[seq_i];
- rev_comp += conversionTable[table_i];
- }
-
- //cout << "seq: " << a_motif << endl;
- //cout << "rc: " << rev_comp << endl;
-
- return rev_comp;
-}
-
-string
-Sequence::motif_normalize(string a_motif)
-{
- string valid_motif;
- int seq_i, len;
-
- len = a_motif.length();
- valid_motif.reserve(len);
-
- // this just upcases IUPAC symbols. Eventually should return an error if non IUPAC is present.
- // current nonIUPAC symbols are omitted, which is not reported atm
- for(seq_i = 0; seq_i < len; seq_i++)
- {
- if ((a_motif[seq_i] == 'a') || (a_motif[seq_i] == 'A'))
- valid_motif += 'A';
- else if ((a_motif[seq_i] == 't') || (a_motif[seq_i] == 'T'))
- valid_motif += 'T';
- else if ((a_motif[seq_i] == 'g') || (a_motif[seq_i] == 'G'))
- valid_motif += 'G';
- else if ((a_motif[seq_i] == 'c') || (a_motif[seq_i] == 'C'))
- valid_motif += 'C';
- else if ((a_motif[seq_i] == 'n') || (a_motif[seq_i] == 'N'))
- valid_motif += 'N';
- else if ((a_motif[seq_i] == 'm') || (a_motif[seq_i] == 'M'))
- valid_motif += 'M';
- else if ((a_motif[seq_i] == 'r') || (a_motif[seq_i] == 'R'))
- valid_motif += 'R';
- else if ((a_motif[seq_i] == 'w') || (a_motif[seq_i] == 'W'))
- valid_motif += 'W';
- else if ((a_motif[seq_i] == 's') || (a_motif[seq_i] == 'S'))
- valid_motif += 'S';
- else if ((a_motif[seq_i] == 'y') || (a_motif[seq_i] == 'Y'))
- valid_motif += 'Y';
- else if ((a_motif[seq_i] == 'k') || (a_motif[seq_i] == 'K'))
- valid_motif += 'G';
- else if ((a_motif[seq_i] == 'v') || (a_motif[seq_i] == 'V'))
- valid_motif += 'V';
- else if ((a_motif[seq_i] == 'h') || (a_motif[seq_i] == 'H'))
- valid_motif += 'H';
- else if ((a_motif[seq_i] == 'd') || (a_motif[seq_i] == 'D'))
- valid_motif += 'D';
- else if ((a_motif[seq_i] == 'b') || (a_motif[seq_i] == 'B'))
- valid_motif += 'B';
- else {
- string msg = "Letter ";
- msg += a_motif[seq_i];
- msg += " is not a valid IUPAC symbol";
- throw motif_normalize_error(msg);
- }
- }
- //cout << "valid_motif is: " << valid_motif << endl;
- return valid_motif;
-}
-
-void Sequence::add_motif(string a_motif)
+void Sequence::add_motif(const Sequence& a_motif)
{
- vector<int> motif_starts = find_motif(a_motif);
+ std::vector<int> motif_starts = find_motif(a_motif);
- for(vector<int>::iterator motif_start_i = motif_starts.begin();
+ for(std::vector<int>::iterator motif_start_i = motif_starts.begin();
motif_start_i != motif_starts.end();
++motif_start_i)
{
- motif_list.push_back(motif(*motif_start_i, a_motif));
+ motif_list.push_back(motif(*motif_start_i, a_motif.get_sequence()));
}
}
motif_list.clear();
}
-const list<motif>& Sequence::motifs() const
+const std::list<motif>& Sequence::motifs() const
{
return motif_list;
}
-vector<int>
-Sequence::find_motif(string a_motif)
+std::vector<int>
+Sequence::find_motif(const Sequence& a_motif) const
{
- vector<int> motif_match_starts;
- string a_motif_rc;
+ std::vector<int> motif_match_starts;
+ Sequence norm_motif_rc;
motif_match_starts.clear();
+ // std::cout << "motif is: " << norm_motif << std::endl;
- //cout << "motif is: " << a_motif << endl;
- a_motif = motif_normalize(a_motif);
- //cout << "motif is: " << a_motif << endl;
-
- if (a_motif != "")
+ if (a_motif.size() > 0)
{
- //cout << "Sequence: none blank motif\n";
+ //std::cout << "Sequence: none blank motif\n";
motif_scan(a_motif, &motif_match_starts);
- a_motif_rc = rc_motif(a_motif);
+ norm_motif_rc = a_motif.rev_comp();;
// make sure not to do search again if it is a palindrome
- if (a_motif_rc != a_motif)
- motif_scan(a_motif_rc, &motif_match_starts);
+ if (norm_motif_rc != a_motif) {
+ motif_scan(norm_motif_rc, &motif_match_starts);
+ }
}
return motif_match_starts;
}
void
-Sequence::motif_scan(string a_motif, vector<int> * motif_match_starts)
+Sequence::motif_scan(const Sequence& a_motif, std::vector<int> * motif_match_starts) const
{
- char * seq_c;
- string::size_type seq_i;
- int motif_i, motif_len;
-
- // faster to loop thru the sequence as a old c string (ie char array)
- seq_c = (char*)sequence.c_str();
- //cout << "Sequence: motif, seq len = " << sequence.length() << endl;
- motif_len = a_motif.length();
+ // if there's no sequence we can't scan for it?
+ // should this throw an exception?
+ if (!seq) return;
- //cout << "motif_length: " << motif_len << endl;
- //cout << "RAAARRRRR\n";
+ std::string::size_type seq_i = 0;
+ Sequence::size_type motif_i = 0;
+ Sequence::size_type motif_len = a_motif.length();
+ Sequence::value_type motif_char;
+ Sequence::value_type seq_char;
- motif_i = 0;
-
- //cout << "motif: " << a_motif << endl;
-
- //cout << "Sequence: motif, length= " << length << endl;
- seq_i = 0;
- while (seq_i < sequence.length())
+ while (seq_i < size())
{
- //cout << seq_c[seq_i];
- //cout << seq_c[seq_i] << "?" << a_motif[motif_i] << ":" << motif_i << " ";
// this is pretty much a straight translation of Nora's python code
// to match iupac letter codes
- if (a_motif[motif_i] =='N')
+ motif_char = toupper(a_motif[motif_i]);
+ seq_char = toupper(seq->at(seq_start+seq_i));
+ if (motif_char =='N')
motif_i++;
- else if (a_motif[motif_i] == seq_c[seq_i])
+ else if (motif_char == seq_char)
motif_i++;
- else if ((a_motif[motif_i] =='M') &&
- ((seq_c[seq_i]=='A') || (seq_c[seq_i]=='C')))
+ else if ((motif_char =='M') && ((seq_char=='A') || (seq_char=='C')))
motif_i++;
- else if ((a_motif[motif_i] =='R') &&
- ((seq_c[seq_i]=='A') || (seq_c[seq_i]=='G')))
+ else if ((motif_char =='R') && ((seq_char=='A') || (seq_char=='G')))
motif_i++;
- else if ((a_motif[motif_i] =='W') &&
- ((seq_c[seq_i]=='A') || (seq_c[seq_i]=='T')))
+ else if ((motif_char =='W') && ((seq_char=='A') || (seq_char=='T')))
motif_i++;
- else if ((a_motif[motif_i] =='S') &&
- ((seq_c[seq_i]=='C') || (seq_c[seq_i]=='G')))
+ else if ((motif_char =='S') && ((seq_char=='C') || (seq_char=='G')))
motif_i++;
- else if ((a_motif[motif_i] =='Y') &&
- ((seq_c[seq_i]=='C') || (seq_c[seq_i]=='T')))
+ else if ((motif_char =='Y') && ((seq_char=='C') || (seq_char=='T')))
motif_i++;
- else if ((a_motif[motif_i] =='K') &&
- ((seq_c[seq_i]=='G') || (seq_c[seq_i]=='T')))
+ else if ((motif_char =='K') && ((seq_char=='G') || (seq_char=='T')))
motif_i++;
- else if ((a_motif[motif_i] =='V') &&
- ((seq_c[seq_i]=='A') || (seq_c[seq_i]=='C') ||
- (seq_c[seq_i]=='G')))
+ else if ((motif_char =='V') &&
+ ((seq_char=='A') || (seq_char=='C') || (seq_char=='G')))
motif_i++;
- else if ((a_motif[seq_i] =='H') &&
- ((seq_c[seq_i]=='A') || (seq_c[seq_i]=='C') ||
- (seq_c[seq_i]=='T')))
+ else if ((motif_char =='H') &&
+ ((seq_char=='A') || (seq_char=='C') || (seq_char=='T')))
motif_i++;
- else if ((a_motif[motif_i] =='D') &&
- ((seq_c[seq_i]=='A') || (seq_c[seq_i]=='G') ||
- (seq_c[seq_i]=='T')))
+ else if ((motif_char =='D') &&
+ ((seq_char=='A') || (seq_char=='G') || (seq_char=='T')))
motif_i++;
- else if ((a_motif[motif_i] =='B') &&
- ((seq_c[seq_i]=='C') || (seq_c[seq_i]=='G') ||
- (seq_c[seq_i]=='T')))
+ else if ((motif_char =='B') &&
+ ((seq_char=='C') || (seq_char=='G') || (seq_char=='T')))
motif_i++;
-
else
{
+ // if a motif doesn't match, erase our current trial and try again
seq_i -= motif_i;
motif_i = 0;
}
// end Nora stuff, now we see if a match is found this pass
if (motif_i == motif_len)
{
- //cout << "!!";
annot new_motif;
motif_match_starts->push_back(seq_i - motif_len + 1);
motif_i = 0;
seq_i++;
}
- //cout << endl;
+ //std::cout << std::endl;
}
+void Sequence::add_string_annotation(std::string a_seq,
+ std::string name)
+{
+ std::vector<int> seq_starts = find_motif(a_seq);
+
+ //std::cout << "searching for " << a_seq << " found " << seq_starts.size() << std::endl;
+
+ for(std::vector<int>::iterator seq_start_i = seq_starts.begin();
+ seq_start_i != seq_starts.end();
+ ++seq_start_i)
+ {
+ annots.push_back(annot(*seq_start_i,
+ *seq_start_i+a_seq.size(),
+ "",
+ name));
+ }
+}
+
+void Sequence::find_sequences(std::list<Sequence>::iterator start,
+ std::list<Sequence>::iterator end)
+{
+ while (start != end) {
+ add_string_annotation(start->get_sequence(), start->get_fasta_header());
+ ++start;
+ }
+}
+
+
+std::ostream& operator<<(std::ostream& out, const Sequence& s)
+{
+ for(Sequence::const_iterator s_i = s.begin(); s_i != s.end(); ++s_i) {
+ out << *s_i;
+ }
+ return out;
+}
+
+bool operator<(const Sequence& x, const Sequence& y)
+{
+ Sequence::const_iterator x_i = x.begin();
+ Sequence::const_iterator y_i = y.begin();
+ // for sequences there's some computation associated with computing .end
+ // so lets cache it.
+ Sequence::const_iterator xend = x.end();
+ Sequence::const_iterator yend = y.end();
+ while(1) {
+ if( x_i == xend and y_i == yend ) {
+ return false;
+ } else if ( x_i == xend ) {
+ return true;
+ } else if ( y_i == yend ) {
+ return false;
+ } else if ( (*x_i) < (*y_i)) {
+ return true;
+ } else if ( (*x_i) > (*y_i) ) {
+ return false;
+ } else {
+ ++x_i;
+ ++y_i;
+ }
+ }
+}
+
+bool operator==(const Sequence& x, const Sequence& y)
+{
+ if (x.empty() and y.empty()) {
+ // if there's no sequence in either sequence structure, they're equal
+ return true;
+ } else if (x.empty() or y.empty()) {
+ // if we fail the first test, and we discover one is empty,
+ // we know they can't be equal. (and we need to do this
+ // to prevent dereferencing an empty pointer)
+ return false;
+ } else if (x.seq_count != y.seq_count) {
+ // if they're of different lenghts, they're not equal
+ return false;
+ }
+ Sequence::const_iterator xseq_i = x.begin();
+ Sequence::const_iterator yseq_i = y.begin();
+ // since the length of the two sequences is equal, we only need to
+ // test one.
+ for(; xseq_i != x.end(); ++xseq_i, ++yseq_i) {
+ if (toupper(*xseq_i) != toupper(*yseq_i)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool operator!=(const Sequence& x, const Sequence& y)
+{
+ return not operator==(x, y);
+}