452f5f98f204d37bf0f85f173eb1f42fc2a07313
[mussa.git] / alg / sequence.cpp
1 //  This file is part of the Mussa source distribution.
2 //  http://mussa.caltech.edu/
3 //  Contact author: Tristan  De Buysscher, tristan@caltech.edu
4
5 // This program and all associated source code files are Copyright (C) 2005
6 // the California Institute of Technology, Pasadena, CA, 91125 USA.  It is
7 // under the GNU Public License; please see the included LICENSE.txt
8 // file for more information, or contact Tristan directly.
9
10
11 //  This file is part of the Mussa source distribution.
12 //  http://mussa.caltech.edu/
13 //  Contact author: Tristan  De Buysscher, tristan@caltech.edu
14
15 // This program and all associated source code files are Copyright (C) 2005
16 // the California Institute of Technology, Pasadena, CA, 91125 USA.  It is
17 // under the GNU Public License; please see the included LICENSE.txt
18 // file for more information, or contact Tristan directly.
19
20
21 //                        ----------------------------------------
22 //                           ---------- sequence.cc -----------
23 //                        ----------------------------------------
24 #include <boost/filesystem/fstream.hpp>
25 #include <boost/filesystem/operations.hpp>
26 namespace fs = boost::filesystem;
27
28 #include <boost/spirit/core.hpp>
29 #include <boost/spirit/actor/push_back_actor.hpp>
30 #include <boost/spirit/iterator/file_iterator.hpp>
31 #include <boost/spirit/utility/chset.hpp>
32 namespace spirit = boost::spirit;
33
34 #include "alg/sequence.hpp"
35 #include "mussa_exceptions.hpp"
36
37 #include <string>
38 #include <stdexcept>
39 #include <iostream>
40 #include <sstream>
41 #include <set>
42
43 annot::annot() 
44  : begin(0),
45    end(0),
46    type(""),
47    name("")
48 {
49 }
50
51 annot::annot(int begin, int end, std::string type, std::string name)
52  : begin(begin),
53    end(end),
54    type(type),
55    name(name)
56 {
57 }
58
59 annot::~annot()
60 {
61 }
62
63 bool operator==(const annot& left, const annot& right)
64 {
65   return ((left.begin== right.begin) and
66           (left.end == right.end) and
67           (left.type == right.type) and
68           (left.name == right.name));
69 }
70
71 motif::motif(int begin, std::string motif)
72  : annot(begin, begin+motif.size(), "motif", motif),
73    sequence(motif)
74 {
75 }
76
77 motif::~motif()
78 {
79 }
80
81
82 Sequence::Sequence(alphabet_ref alphabet_)
83   : parent(0),
84     alphabet(alphabet_),
85     seq_start(0),
86     seq_count(0),
87     strand(UnknownStrand)
88 {
89 }
90
91 Sequence::~Sequence()
92 {
93 }
94
95 Sequence::Sequence(const char *seq, alphabet_ref alphabet_)
96   : parent(0),
97     alphabet(alphabet_),
98     seq_start(0),
99     seq_count(0),
100     strand(UnknownStrand),
101     header(""),
102     species("")
103 {
104   set_filtered_sequence(seq, alphabet);
105 }
106
107 Sequence::Sequence(const std::string& seq, alphabet_ref alphabet_) 
108   : parent(0),
109     alphabet(alphabet_),
110     seq_start(0),
111     seq_count(0),
112     strand(UnknownStrand),
113     header(""),
114     species("")
115 {
116   set_filtered_sequence(seq, alphabet);
117 }
118
119 Sequence::Sequence(const Sequence& o)
120   : parent(o.parent),
121     seq(o.seq),
122     alphabet(o.alphabet),
123     seq_start(o.seq_start),
124     seq_count(o.seq_count),
125     strand(o.strand),
126     header(o.header),
127     species(o.species),
128     annots(o.annots),
129     motif_list(o.motif_list)
130 {
131 }
132
133 Sequence &Sequence::operator=(const Sequence& s)
134 {
135   if (this != &s) {
136     parent = s.parent;
137     seq = s.seq;
138     alphabet = s.alphabet;
139     seq_start = s.seq_start;
140     seq_count = s.seq_count;
141     strand = s.strand;
142     header = s.header;
143     species = s.species;
144     annots = s.annots;
145     motif_list = s.motif_list;
146   }
147   return *this;
148 }
149
150 static void multiplatform_getline(std::istream& in, std::string& line)
151 {
152   line.clear();
153   char c;
154   in.get(c);
155   while(in.good() and !(c == '\012' or c == '\015') ) {
156     line.push_back(c);
157     in.get(c);
158   }
159   // if we have cr-lf eat it
160   c = in.peek();
161   if (c=='\012' or c == '\015') {
162     in.get();
163   }
164 }
165
166 void Sequence::load_fasta(fs::path file_path, int seq_num, int start_index, int end_index)
167 {
168   load_fasta(file_path, alphabet, seq_num, start_index, end_index);
169 }
170
171 //! load a fasta file into a sequence
172 void Sequence::load_fasta(fs::path file_path, alphabet_ref a, 
173                           int seq_num, int start_index, int end_index)
174 {
175   fs::fstream data_file;
176   data_file.open(file_path, std::ios::in);
177
178   if (!data_file.good())
179   {    
180     throw mussa_load_error("Sequence File: "+file_path.string()+" not found"); 
181   } else {
182     try {
183       load_fasta(data_file, a, seq_num, start_index, end_index);
184     } catch(sequence_empty_error e) {
185       // there doesn't appear to be any sequence
186       // catch and rethrow to include the filename
187       std::stringstream msg;
188       msg << "The selected sequence in " 
189           << file_path.native_file_string()
190           << " appears to be empty"; 
191       throw sequence_empty_error(msg.str());
192     } catch(sequence_empty_file_error e) {
193       std::stringstream errormsg;
194       errormsg << file_path.native_file_string()
195                << " did not have any fasta sequences" << std::endl;
196       throw sequence_empty_file_error(errormsg.str());
197     } catch(sequence_invalid_load_error e) {
198       std::ostringstream msg;
199       msg << file_path.native_file_string();
200       msg << " " << e.what();
201       throw sequence_invalid_load_error(msg.str());
202     }
203   }
204 }
205
206 void Sequence::load_fasta(std::istream& file, 
207                           int seq_num, int start_index, int end_index)
208 {
209   load_fasta(file, alphabet, seq_num, start_index, end_index);
210 }
211
212 void
213 Sequence::load_fasta(std::istream& data_file, alphabet_ref a, 
214                      int seq_num, 
215                      int start_index, int end_index)
216 {
217   std::string file_data_line;
218   int header_counter = 0;
219   size_t line_counter = 0;
220   bool read_seq = true;
221   std::string rev_comp;
222   std::string sequence_raw;
223   std::string seq_tmp;      // holds sequence during basic filtering
224   const Alphabet &alpha = get_alphabet(a);
225
226   if (seq_num == 0) {
227     throw mussa_load_error("fasta sequence number is 1 based (can't be 0)");
228   }
229
230   // search for the header of the fasta sequence we want
231   while ( (!data_file.eof()) && (header_counter < seq_num) )
232   {
233     multiplatform_getline(data_file, file_data_line);
234     ++line_counter;
235     if (file_data_line.substr(0,1) == ">")
236       header_counter++;
237   }
238
239   if (header_counter > 0) {
240     header = file_data_line.substr(1);
241
242     sequence_raw = "";
243
244     while ( !data_file.eof() && read_seq ) {
245       multiplatform_getline(data_file,file_data_line);
246       ++line_counter;
247       if (file_data_line.substr(0,1) == ">")
248         read_seq = false;
249       else {
250         for (std::string::const_iterator line_i = file_data_line.begin();
251              line_i != file_data_line.end();
252              ++line_i)
253          {
254            if(alpha.exists(*line_i)) {
255              sequence_raw += *line_i;
256            } else {
257             std::ostringstream msg;
258             msg << "Unrecognized characters in fasta sequence at line ";
259             msg << line_counter;
260             throw sequence_invalid_load_error(msg.str());
261            }
262          }
263       }
264     }
265
266     // Lastly, if subselection of the sequence was specified we keep cut out
267     // and only keep that part
268     // end_index = 0 means no end was specified, so cut to the end 
269     if (end_index == 0)
270       end_index = sequence_raw.size();
271
272     // sequence filtering for upcasing agctn and convert non AGCTN to N
273     if (end_index-start_index <= 0) {
274       std::string msg("The selected sequence appears to be empty"); 
275       throw sequence_empty_error(msg);
276     }
277     set_filtered_sequence(sequence_raw, a, start_index, end_index-start_index);
278   } else {
279     std::string errormsg("There were no fasta sequences");
280     throw sequence_empty_file_error(errormsg);
281   }
282 }
283
284 void Sequence::set_filtered_sequence(const std::string &in_seq,
285                                      alphabet_ref alphabet_,
286                                      size_type start,
287                                      size_type count,
288                                      strand_type strand_)
289 {
290   alphabet = alphabet_;
291   if ( count == npos)
292     count = in_seq.size() - start;
293   boost::shared_ptr<seq_string> new_seq(new seq_string);
294   new_seq->reserve(count);
295
296   // finally, the actual conversion loop
297   const Alphabet& alpha_impl = get_alphabet(); // go get one of our actual alphabets
298   std::string::const_iterator seq_i = in_seq.begin()+start;
299   for(size_type i = 0; i != count; ++i, ++seq_i)
300   {
301     if (alpha_impl.exists(*seq_i)) {
302       new_seq->append(1, toupper(*seq_i));
303     } else {
304       new_seq->append(1, 'N');
305     }
306   }
307   parent = 0;
308   seq = new_seq;
309   seq_start = 0;
310   seq_count = count;
311   strand = strand_;
312 }
313
314 void
315 Sequence::load_annot(fs::path file_path, int start_index, int end_index)
316 {
317   if (not fs::exists(file_path)) {
318     throw mussa_load_error("Annotation File " + file_path.string() + " was not found");
319   }
320   if (fs::is_directory(file_path)) {
321     throw mussa_load_error(file_path.string() +
322             " is a directory, please provide a file for annotations."
323           );
324   }    
325   fs::fstream data_stream(file_path, std::ios::in);
326   if (!data_stream)
327   {
328     throw mussa_load_error("Error loading annotation file " + file_path.string());
329   }
330
331   // so i should probably be passing the parse function some iterators
332   // but the annotations files are (currently) small, so i think i can 
333   // get away with loading the whole file into memory
334   std::string data;
335   char c;
336   while(data_stream.good()) {
337     data_stream.get(c);
338     data.push_back(c);
339   }
340   data_stream.close();
341
342   try {  
343     parse_annot(data, start_index, end_index);
344   } catch(annotation_load_error e) {
345     std::ostringstream msg;
346     msg << file_path.native_file_string()
347         << " "
348         << e.what();
349     throw annotation_load_error(msg.str());
350   }
351 }
352
353 /* If this works, yikes, this is some brain hurting code.
354  *
355  * what's going on is that when pb_annot is instantiated it stores references
356  * to begin, end, name, type, declared in the parse function, then
357  * when operator() is called it grabs values from those references
358  * and uses that to instantiate an annot object and append that to our
359  * annotation list.
360  *
361  * This weirdness is because the spirit library requires that actions
362  * conform to a specific prototype operator()(IteratorT, IteratorT)
363  * which doesn't provide any useful opportunity for me to actually
364  * grab the results of our parsing.
365  *
366  * so I instantiate this structure in order to have a place to grab
367  * my data from.
368  */
369   
370 struct push_back_annot {
371   std::list<annot>& annot_list;
372   int& begin;
373   int& end;
374   std::string& name;
375   std::string& type;
376   int &parsed;
377
378   push_back_annot(std::list<annot>& annot_list_, 
379                   int& begin_, 
380                   int& end_, 
381                   std::string& name_, 
382                   std::string& type_,
383                   int &parsed_) 
384   : annot_list(annot_list_), 
385     begin(begin_),
386     end(end_),
387     name(name_),
388     type(type_),
389     parsed(parsed_)
390   {
391   }
392
393   void operator()(std::string::const_iterator, 
394                   std::string::const_iterator) const 
395   {
396     //std::cout << "adding annot: " << begin << "|" << end << "|" << name << "|" << type << std::endl;
397     annot_list.push_back(annot(begin, end, name, type));
398     ++parsed;
399   };
400 };
401
402 struct push_back_seq {
403   std::list<Sequence>& seq_list;
404   std::string& name;
405   std::string& seq;
406   int &parsed;
407
408   push_back_seq(std::list<Sequence>& seq_list_,
409                 std::string& name_, 
410                 std::string& seq_,
411                 int &parsed_)
412   : seq_list(seq_list_), 
413     name(name_),
414     seq(seq_),
415     parsed(parsed_)
416   {
417   }
418
419   void operator()(std::string::const_iterator, 
420                   std::string::const_iterator) const 
421   {
422     // filter out newlines from our sequence
423     std::string new_seq;
424     for(std::string::const_iterator seq_i = seq.begin();
425         seq_i != seq.end();
426         ++seq_i)
427     {
428       if (*seq_i != '\015' && *seq_i != '\012') new_seq += *seq_i;
429     }
430     //std::cout << "adding seq: " << name << " " << new_seq << std::endl;
431     
432     Sequence s(new_seq);
433     s.set_fasta_header(name);
434     seq_list.push_back(s);
435     ++parsed;
436   };
437 };
438
439 void
440 Sequence::parse_annot(std::string data, int start_index, int end_index)
441 {
442   int start=0;
443   int end=0;
444   std::string name;
445   std::string type;
446   std::string seq;
447   std::list<annot> parsed_annots;
448   std::list<Sequence> query_seqs;
449   int parsed=0;
450
451   bool ok = spirit::parse(data.begin(), data.end(),
452               (
453                //begin grammar
454                  !(
455                     (
456                       spirit::alpha_p >> 
457                       +(spirit::graph_p)
458                     )[spirit::assign_a(species)] >> 
459                     +(spirit::space_p)
460                   ) >>
461                   *(
462                      ( // ignore html tags
463                        *(spirit::space_p) >>
464                        spirit::ch_p('<') >> 
465                        +(~spirit::ch_p('>')) >>
466                        spirit::ch_p('>') >>
467                        *(spirit::space_p)
468                      )
469                    |
470                     ( // parse an absolute location name
471                      (spirit::uint_p[spirit::assign_a(start)] >> 
472                       +spirit::space_p >>
473                       spirit::uint_p[spirit::assign_a(end)] >> 
474                       +spirit::space_p >>
475                       ( 
476                          spirit::alpha_p >> 
477                          *spirit::graph_p
478                       )[spirit::assign_a(name)] >> 
479                       // optional type
480                       !(
481                           +spirit::space_p >>
482                           (
483                             spirit::alpha_p >>
484                             *spirit::graph_p
485                           )[spirit::assign_a(type)]
486                       )
487                       // to understand how this group gets set
488                       // read the comment above struct push_back_annot
489                      )[push_back_annot(parsed_annots, start, end, type, name, parsed)]
490                    |
491                     ((spirit::ch_p('>')|spirit::str_p("&gt;")) >> 
492                        (*(spirit::print_p))[spirit::assign_a(name)] >>
493                        spirit::eol_p >> 
494                        (+(spirit::chset<>(Alphabet::nucleic_cstr)))[spirit::assign_a(seq)]
495                      )[push_back_seq(query_seqs, name, seq, parsed)]
496                     ) >>
497                     *spirit::space_p
498                    )
499               //end grammar
500               )).full;
501   if (not ok) {
502     std::stringstream msg;
503     msg << "Error parsing annotation #" << parsed;
504     throw annotation_load_error(msg.str());
505   }
506   // add newly parsed annotations to our sequence
507   std::copy(parsed_annots.begin(), parsed_annots.end(), std::back_inserter(annots));
508   // go seearch for query sequences 
509   find_sequences(query_seqs.begin(), query_seqs.end());
510 }
511
512 void Sequence::add_annotation(const annot& a)
513 {
514   annots.push_back(a);
515 }
516
517 const std::list<annot>& Sequence::annotations() const
518 {
519   return annots;
520 }
521
522 Sequence
523 Sequence::subseq(int start, int count)
524 {
525   if (!seq) {
526     Sequence new_seq;
527     return new_seq;
528   }
529
530   // there might be an off by one error with start+count > size()
531   if ( count == npos || start+count > size()) {
532     count = size()-start;
533   }
534   Sequence new_seq(*this);
535   new_seq.parent = this;
536   new_seq.seq_start = seq_start+start;
537   new_seq.seq_count = count;
538
539   new_seq.motif_list = motif_list;
540   new_seq.annots.clear();
541   // attempt to copy & reannotate position based annotations 
542   int end = start+count;
543
544   for(std::list<annot>::const_iterator annot_i = annots.begin();
545       annot_i != annots.end();
546       ++annot_i)
547   {
548     int annot_begin= annot_i->begin;
549     int annot_end = annot_i->end;
550
551     if (annot_begin < end) {
552       if (annot_begin >= start) {
553         annot_begin -= start;
554       } else {
555         annot_begin = 0;
556       }
557
558       if (annot_end < end) {
559         annot_end -= start;
560       } else {
561         annot_end = count;
562       }
563
564       annot new_annot(annot_begin, annot_end, annot_i->type, annot_i->name);
565       new_seq.annots.push_back(new_annot);
566     }
567   }
568
569   return new_seq;
570 }
571
572 std::string Sequence::create_reverse_map() const 
573 {
574   std::string rc_map(256, '~');
575   // if we're rna, use U instead of T
576   // we might want to add an "is_rna" to sequence at somepoint
577   char TU = (alphabet == reduced_rna_alphabet) ? 'U' : 'T';
578   char tu = (alphabet == reduced_rna_alphabet) ? 'u' : 't';
579   rc_map['A'] = TU ; rc_map['a'] = tu ;
580   rc_map['T'] = 'A'; rc_map['t'] = 'a';
581   rc_map['U'] = 'A'; rc_map['u'] = 'a';
582   rc_map['G'] = 'C'; rc_map['g'] = 'c';
583   rc_map['C'] = 'G'; rc_map['c'] = 'g';
584   rc_map['M'] = 'K'; rc_map['m'] = 'k';
585   rc_map['R'] = 'Y'; rc_map['r'] = 'y';
586   rc_map['W'] = 'W'; rc_map['w'] = 'w';
587   rc_map['S'] = 'S'; rc_map['s'] = 's';
588   rc_map['Y'] = 'R'; rc_map['y'] = 'r';
589   rc_map['K'] = 'M'; rc_map['k'] = 'm';
590   rc_map['V'] = 'B'; rc_map['v'] = 'b';
591   rc_map['H'] = 'D'; rc_map['h'] = 'd';
592   rc_map['D'] = 'H'; rc_map['d'] = 'h';
593   rc_map['B'] = 'V'; rc_map['b'] = 'v';  
594   rc_map['N'] = 'N'; rc_map['n'] = 'n';
595   rc_map['X'] = 'X'; rc_map['x'] = 'x';
596   rc_map['?'] = '?'; 
597   rc_map['.'] = '.'; 
598   rc_map['-'] = '-'; 
599   rc_map['~'] = '~'; // not really needed, but perhaps it's clearer. 
600   return rc_map;
601 }
602
603 Sequence Sequence::rev_comp() const
604 {
605   std::string rev_comp;
606   rev_comp.reserve(length());
607   
608   std::string rc_map = create_reverse_map();
609
610   // reverse and convert
611   Sequence::const_reverse_iterator seq_i;
612   Sequence::const_reverse_iterator seq_end = rend();  
613   for(seq_i = rbegin(); 
614       seq_i != seq_end;
615       ++seq_i)
616   {
617     rev_comp.append(1, rc_map[*seq_i]);
618   }
619   return Sequence(rev_comp, alphabet); 
620 }
621
622 void Sequence::set_fasta_header(std::string header_)
623 {
624   header = header_;
625 }
626
627 void Sequence::set_species(const std::string& name)
628 {
629   species = name;
630 }
631
632 std::string Sequence::get_species() const
633 {
634   return species;
635 }
636
637
638 std::string
639 Sequence::get_fasta_header() const
640 {
641   return header;
642 }
643
644 std::string
645 Sequence::get_name() const
646 {
647   if (header.size() > 0) 
648     return header;
649   else if (species.size() > 0)
650     return species;
651   else
652     return "";
653 }
654
655 const Alphabet& Sequence::get_alphabet() const
656 {
657   return get_alphabet(alphabet);
658 }
659
660 const Alphabet& Sequence::get_alphabet(alphabet_ref alpha) const
661 {
662   switch (alpha) {
663     case reduced_dna_alphabet:
664       return Alphabet::reduced_dna_alphabet();
665     case reduced_rna_alphabet:
666       return Alphabet::reduced_rna_alphabet();
667     case reduced_nucleic_alphabet:
668       return Alphabet::reduced_nucleic_alphabet();
669     case nucleic_alphabet:
670       return Alphabet::nucleic_alphabet();
671     case protein_alphabet:
672       return Alphabet::protein_alphabet();    
673     default:
674       throw std::runtime_error("unrecognized alphabet type");
675       break;
676   }
677 }
678
679 void Sequence::set_sequence(const std::string& s, alphabet_ref a) 
680 {
681   set_filtered_sequence(s, a);
682 }
683
684 std::string Sequence::get_sequence() const
685 {
686   if (seq) 
687     return *seq;
688   else
689     return std::string();
690 }
691
692 Sequence::const_reference Sequence::operator[](Sequence::size_type i) const
693 {
694   return at(i);
695 }
696
697 Sequence::const_reference Sequence::at(Sequence::size_type i) const
698 {
699   if (!seq) throw std::out_of_range("empty sequence");
700   return seq->at(i+seq_start);
701 }
702
703 void
704 Sequence::clear()
705 {
706   parent = 0;
707   seq.reset();
708   seq_start = 0;
709   seq_count = 0;
710   strand = UnknownStrand;
711   header.clear();
712   species.clear();
713   annots.clear();
714   motif_list.clear();
715 }
716
717 const char *Sequence::c_str() const
718 {
719   if (seq) 
720     return seq->c_str()+seq_start;
721   else 
722     return 0;
723 }
724
725 Sequence::const_iterator Sequence::begin() const
726 {
727   if (seq and seq_count != 0)
728     return seq->begin()+seq_start;
729   else 
730     return Sequence::const_iterator(0);
731 }
732
733 Sequence::const_iterator Sequence::end() const
734 {
735   if (seq and seq_count != 0) {
736     return seq->begin() + seq_start + seq_count;
737   } else {
738     return Sequence::const_iterator(0);
739   }
740 }
741
742 Sequence::const_reverse_iterator Sequence::rbegin() const
743 {
744   if (seq and seq_count != 0)
745     return seq->rbegin()+(seq->size()-(seq_start+seq_count));
746   else 
747     return Sequence::const_reverse_iterator();
748 }
749
750 Sequence::const_reverse_iterator Sequence::rend() const
751 {
752   if (seq and seq_count != 0) {
753     return rbegin() + seq_count;
754   } else {
755     return Sequence::const_reverse_iterator();
756   }
757 }
758
759 bool Sequence::empty() const
760 {
761   return (seq_count == 0) ? true : false;
762 }
763
764 Sequence::size_type Sequence::find_first_not_of(
765   const std::string& query, 
766   Sequence::size_type index)
767 {
768   typedef std::set<std::string::value_type> sequence_set;
769   sequence_set match_set;
770   
771   for(const_iterator query_item = query.begin();
772       query_item != query.end();
773       ++query_item)
774   {
775     match_set.insert(*query_item);
776   }  
777   for(const_iterator base = begin();
778       base != end();
779       ++base)
780   {
781     if(match_set.find(*base) == match_set.end()) {
782       return base-begin();
783     } 
784   }
785   return Sequence::npos;
786 }
787  
788 Sequence::size_type Sequence::start() const
789 {
790   if (parent)
791     return seq_start - parent->start();
792   else
793     return seq_start;
794 }
795
796 Sequence::size_type Sequence::stop() const
797 {
798   return start() + seq_count;
799 }
800
801 Sequence::size_type Sequence::size() const
802 {
803   return seq_count;
804 }
805
806 Sequence::size_type Sequence::length() const
807 {
808   return size();
809 }
810
811 void
812 Sequence::save(fs::fstream &save_file)
813 {
814   //fstream save_file;
815   std::list<annot>::iterator annots_i;
816
817   // not sure why, or if i'm doing something wrong, but can't seem to pass
818   // file pointers down to this method from the mussa control class
819   // so each call to save a sequence appends to the file started by mussa_class
820   //save_file.open(save_file_path.c_str(), std::ios::app);
821
822   save_file << "<Sequence>" << std::endl;
823   save_file << *this << std::endl;
824   save_file << "</Sequence>" << std::endl;
825
826   save_file << "<Annotations>" << std::endl;
827   save_file << species << std::endl;
828   for (annots_i = annots.begin(); annots_i != annots.end(); ++annots_i)
829   {
830     save_file << annots_i->begin << " " << annots_i->end << " " ;
831     save_file << annots_i->name << " " << annots_i->type << std::endl;
832   }
833   save_file << "</Annotations>" << std::endl;
834   //save_file.close();
835 }
836
837 void
838 Sequence::load_museq(fs::path load_file_path, int seq_num)
839 {
840   fs::fstream load_file;
841   std::string file_data_line;
842   int seq_counter;
843   annot an_annot;
844   std::string::size_type space_split_i;
845   std::string annot_value;
846
847   annots.clear();
848   load_file.open(load_file_path, std::ios::in);
849
850   seq_counter = 0;
851   // search for the seq_num-th sequence 
852   while ( (!load_file.eof()) && (seq_counter < seq_num) )
853   {
854     getline(load_file,file_data_line);
855     if (file_data_line == "<Sequence>")
856       seq_counter++;
857   }
858   getline(load_file, file_data_line);
859   // looks like the sequence is written as a single line
860   set_filtered_sequence(file_data_line, reduced_dna_alphabet);
861   getline(load_file, file_data_line);
862   getline(load_file, file_data_line);
863   if (file_data_line == "<Annotations>")
864   {
865     getline(load_file, file_data_line);
866     species = file_data_line;
867     while ( (!load_file.eof())  && (file_data_line != "</Annotations>") )
868     {
869       getline(load_file,file_data_line);
870       if ((file_data_line != "") && (file_data_line != "</Annotations>"))  
871       {
872         // need to get 4 values...almost same code 4 times...
873         // get annot start index
874         space_split_i = file_data_line.find(" ");
875         annot_value = file_data_line.substr(0,space_split_i);
876         an_annot.begin = atoi (annot_value.c_str());
877         file_data_line = file_data_line.substr(space_split_i+1);
878         // get annot end index
879         space_split_i = file_data_line.find(" ");
880         annot_value = file_data_line.substr(0,space_split_i);
881         an_annot.end = atoi (annot_value.c_str());
882
883         if (space_split_i == std::string::npos)  // no entry for type or name
884         {
885           std::cout << "seq, annots - no type or name\n";
886           an_annot.type = "";
887           an_annot.name = "";
888         }
889         else   // else get annot type
890         {
891           file_data_line = file_data_line.substr(space_split_i+1);
892           space_split_i = file_data_line.find(" ");
893           annot_value = file_data_line.substr(0,space_split_i);
894           an_annot.type = annot_value;
895           if (space_split_i == std::string::npos)  // no entry for name
896           {
897             std::cout << "seq, annots - no name\n";
898             an_annot.name = "";
899           }
900           else          // get annot name
901           {
902             file_data_line = file_data_line.substr(space_split_i+1);
903             space_split_i = file_data_line.find(" ");
904             annot_value = file_data_line.substr(0,space_split_i);
905             an_annot.type = annot_value;
906           }
907         }
908         annots.push_back(an_annot);  // don't forget to actually add the annot
909       }
910       //std::cout << "seq, annots: " << an_annot.start << ", " << an_annot.end
911       //     << "-->" << an_annot.type << "::" << an_annot.name << std::endl;
912     }
913   }
914   load_file.close();
915 }
916
917
918 void Sequence::add_motif(const Sequence& a_motif)
919 {
920   std::vector<int> motif_starts = find_motif(a_motif);
921
922   for(std::vector<int>::iterator motif_start_i = motif_starts.begin();
923       motif_start_i != motif_starts.end();
924       ++motif_start_i)
925   {
926     motif_list.push_back(motif(*motif_start_i, a_motif.get_sequence()));
927   }
928 }
929
930 void Sequence::clear_motifs()
931 {
932   motif_list.clear();
933 }
934
935 const std::list<motif>& Sequence::motifs() const
936 {
937   return motif_list;
938 }
939
940 std::vector<int>
941 Sequence::find_motif(const Sequence& a_motif) const
942 {
943   std::vector<int> motif_match_starts;
944   Sequence norm_motif_rc;
945
946   motif_match_starts.clear();
947   // std::cout << "motif is: " << norm_motif << std::endl;
948
949   if (a_motif.size() > 0)
950   {
951     //std::cout << "Sequence: none blank motif\n";
952     motif_scan(a_motif, &motif_match_starts);
953
954     norm_motif_rc = a_motif.rev_comp();;
955     // make sure not to do search again if it is a palindrome
956     if (norm_motif_rc != a_motif) {
957       motif_scan(norm_motif_rc, &motif_match_starts);
958     }
959   }
960   return motif_match_starts;
961 }
962
963 void
964 Sequence::motif_scan(const Sequence& a_motif, std::vector<int> * motif_match_starts) const
965 {
966   // if there's no sequence we can't scan for it?
967   // should this throw an exception?
968   if (!seq) return;
969
970   std::string::size_type seq_i = 0;
971   Sequence::size_type motif_i = 0;
972   Sequence::size_type motif_len = a_motif.length();
973   Sequence::value_type motif_char;
974   Sequence::value_type seq_char;
975
976   while (seq_i < size())
977   {
978     // this is pretty much a straight translation of Nora's python code
979     // to match iupac letter codes
980     motif_char = toupper(a_motif[motif_i]);
981     seq_char = toupper(seq->at(seq_start+seq_i));
982     if (motif_char =='N')
983       motif_i++;
984     else if (motif_char == seq_char)
985       motif_i++;
986     else if ((motif_char =='M') && ((seq_char=='A') || (seq_char=='C')))
987       motif_i++;
988     else if ((motif_char =='R') && ((seq_char=='A') || (seq_char=='G')))
989       motif_i++;
990     else if ((motif_char =='W') && ((seq_char=='A') || (seq_char=='T')))
991       motif_i++;
992     else if ((motif_char =='S') && ((seq_char=='C') || (seq_char=='G')))
993       motif_i++;
994     else if ((motif_char =='Y') && ((seq_char=='C') || (seq_char=='T')))
995       motif_i++;
996     else if ((motif_char =='K') && ((seq_char=='G') || (seq_char=='T')))
997       motif_i++;
998     else if ((motif_char =='V') && 
999              ((seq_char=='A') || (seq_char=='C') || (seq_char=='G')))
1000       motif_i++;
1001     else if ((motif_char =='H') && 
1002              ((seq_char=='A') || (seq_char=='C') || (seq_char=='T')))
1003       motif_i++;
1004     else if ((motif_char =='D') &&
1005              ((seq_char=='A') || (seq_char=='G') || (seq_char=='T')))
1006       motif_i++;
1007     else if ((motif_char =='B') &&
1008              ((seq_char=='C') || (seq_char=='G') || (seq_char=='T')))
1009       motif_i++;
1010     else
1011     {
1012       // if a motif doesn't match, erase our current trial and try again
1013       seq_i -= motif_i;
1014       motif_i = 0;
1015     }
1016
1017     // end Nora stuff, now we see if a match is found this pass
1018     if (motif_i == motif_len)
1019     {
1020       annot new_motif;
1021       motif_match_starts->push_back(seq_i - motif_len + 1);
1022       motif_i = 0;
1023     }
1024
1025     seq_i++;
1026   }
1027   //std::cout << std::endl;
1028 }
1029
1030 void Sequence::add_string_annotation(std::string a_seq, 
1031                                      std::string name)
1032 {
1033   std::vector<int> seq_starts = find_motif(a_seq);
1034   
1035   //std::cout << "searching for " << a_seq << " found " << seq_starts.size() << std::endl;
1036
1037   for(std::vector<int>::iterator seq_start_i = seq_starts.begin();
1038       seq_start_i != seq_starts.end();
1039       ++seq_start_i)
1040   {
1041     annots.push_back(annot(*seq_start_i, 
1042                            *seq_start_i+a_seq.size(),
1043                            "",
1044                            name));
1045   }
1046 }
1047
1048 void Sequence::find_sequences(std::list<Sequence>::iterator start, 
1049                               std::list<Sequence>::iterator end)
1050 {
1051   while (start != end) {
1052     add_string_annotation(start->get_sequence(), start->get_fasta_header());
1053     ++start;
1054   }
1055 }
1056
1057
1058 std::ostream& operator<<(std::ostream& out, const Sequence& s)
1059 {
1060   for(Sequence::const_iterator s_i = s.begin(); s_i != s.end(); ++s_i) {
1061     out << *s_i;
1062   }
1063   return out;
1064 }
1065
1066 bool operator<(const Sequence& x, const Sequence& y)
1067 {
1068   Sequence::const_iterator x_i = x.begin();
1069   Sequence::const_iterator y_i = y.begin();
1070   // for sequences there's some computation associated with computing .end
1071   // so lets cache it.
1072   Sequence::const_iterator xend = x.end();
1073   Sequence::const_iterator yend = y.end();
1074   while(1) {
1075     if( x_i == xend and y_i == yend ) {
1076       return false;
1077     } else if ( x_i == xend ) {
1078       return true;
1079     } else if ( y_i == yend ) {
1080       return false;
1081     } else if ( (*x_i) < (*y_i)) {
1082       return true;
1083     } else if ( (*x_i) > (*y_i) ) {
1084       return false;
1085     } else {
1086       ++x_i;
1087       ++y_i;
1088     }
1089   }
1090 }
1091
1092 bool operator==(const Sequence& x, const Sequence& y) 
1093 {
1094   if (x.empty() and y.empty()) {
1095     // if there's no sequence in either sequence structure, they're equal
1096     return true;
1097   } else if (x.empty() or y.empty()) {
1098     // if we fail the first test, and we discover one is empty,
1099     // we know they can't be equal. (and we need to do this
1100     // to prevent dereferencing an empty pointer)
1101     return false;
1102   } else if (x.seq_count != y.seq_count) {
1103     // if they're of different lenghts, they're not equal
1104     return false;
1105   }
1106   Sequence::const_iterator xseq_i = x.begin();
1107   Sequence::const_iterator yseq_i = y.begin();
1108   // since the length of the two sequences is equal, we only need to
1109   // test one.
1110   for(; xseq_i != x.end(); ++xseq_i, ++yseq_i) {
1111     if (toupper(*xseq_i) != toupper(*yseq_i)) {
1112       return false;
1113     }
1114   }
1115   return true;
1116 }
1117
1118 bool operator!=(const Sequence& x, const Sequence& y) 
1119 {
1120   return not operator==(x, y);
1121 }