Move alphabet type into SeqString
[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(AlphabetRef alphabet)
83   : seq(new SeqSpan("", alphabet)),
84     strand(UnknownStrand)
85 {
86 }
87
88 Sequence::~Sequence()
89 {
90 }
91
92 Sequence::Sequence(const char *seq, AlphabetRef alphabet_)
93   : strand(UnknownStrand),
94     header(""),
95     species("")
96 {
97   set_filtered_sequence(seq, alphabet_);
98 }
99
100 Sequence::Sequence(const std::string& seq, AlphabetRef alphabet_) 
101   : strand(UnknownStrand),
102     header(""),
103     species("")
104 {
105   set_filtered_sequence(seq, alphabet_);
106 }
107
108 Sequence::Sequence(const Sequence& o)
109   : seq(o.seq),
110     strand(o.strand),
111     header(o.header),
112     species(o.species),
113     annots(o.annots),
114     motif_list(o.motif_list)
115 {
116 }
117
118 Sequence::Sequence(const Sequence* o)
119   : seq(o->seq),
120     strand(o->strand),
121     header(o->header),
122     species(o->species),
123     annots(o->annots),
124     motif_list(o->motif_list)
125 {
126 }
127
128 Sequence::Sequence(const SeqSpanRef& seq_ref)
129   : seq(seq_ref),
130     strand(UnknownStrand),
131     header(""),
132     species("")
133 {
134 }
135
136 Sequence &Sequence::operator=(const Sequence& s)
137 {
138   if (this != &s) {
139     seq = s.seq;
140     strand = s.strand;
141     header = s.header;
142     species = s.species;
143     annots = s.annots;
144     motif_list = s.motif_list;
145   }
146   return *this;
147 }
148
149 static void multiplatform_getline(std::istream& in, std::string& line)
150 {
151   line.clear();
152   char c;
153   in.get(c);
154   while(in.good() and !(c == '\012' or c == '\015') ) {
155     line.push_back(c);
156     in.get(c);
157   }
158   // if we have cr-lf eat it
159   c = in.peek();
160   if (c=='\012' or c == '\015') {
161     in.get();
162   }
163 }
164
165 void Sequence::load_fasta(fs::path file_path, int seq_num, int start_index, int end_index)
166 {
167   load_fasta(file_path, reduced_nucleic_alphabet, seq_num, start_index, end_index);
168 }
169
170 //! load a fasta file into a sequence
171 void Sequence::load_fasta(fs::path file_path, AlphabetRef a, 
172                           int seq_num, int start_index, int end_index)
173 {
174   fs::fstream data_file;
175   data_file.open(file_path, std::ios::in);
176
177   if (!data_file.good())
178   {    
179     throw mussa_load_error("Sequence File: "+file_path.string()+" not found"); 
180   } else {
181     try {
182       load_fasta(data_file, a, seq_num, start_index, end_index);
183     } catch(sequence_empty_error e) {
184       // there doesn't appear to be any sequence
185       // catch and rethrow to include the filename
186       std::stringstream msg;
187       msg << "The selected sequence in " 
188           << file_path.native_file_string()
189           << " appears to be empty"; 
190       throw sequence_empty_error(msg.str());
191     } catch(sequence_empty_file_error e) {
192       std::stringstream errormsg;
193       errormsg << file_path.native_file_string()
194                << " did not have any fasta sequences" << std::endl;
195       throw sequence_empty_file_error(errormsg.str());
196     } catch(sequence_invalid_load_error e) {
197       std::ostringstream msg;
198       msg << file_path.native_file_string();
199       msg << " " << e.what();
200       throw sequence_invalid_load_error(msg.str());
201     }
202   }
203 }
204
205 void Sequence::load_fasta(std::istream& file, 
206                           int seq_num, int start_index, int end_index)
207 {
208   load_fasta(file, reduced_nucleic_alphabet, seq_num, start_index, end_index);
209 }
210
211 void
212 Sequence::load_fasta(std::istream& data_file, AlphabetRef a, 
213                      int seq_num, 
214                      int start_index, int end_index)
215 {
216   std::string file_data_line;
217   int header_counter = 0;
218   size_t line_counter = 0;
219   bool read_seq = true;
220   std::string rev_comp;
221   std::string sequence_raw;
222   std::string seq_tmp;      // holds sequence during basic filtering
223   const Alphabet &alpha = get_alphabet();
224
225   if (seq_num == 0) {
226     throw mussa_load_error("fasta sequence number is 1 based (can't be 0)");
227   }
228
229   // search for the header of the fasta sequence we want
230   while ( (!data_file.eof()) && (header_counter < seq_num) )
231   {
232     multiplatform_getline(data_file, file_data_line);
233     ++line_counter;
234     if (file_data_line.substr(0,1) == ">")
235       header_counter++;
236   }
237
238   if (header_counter > 0) {
239     header = file_data_line.substr(1);
240
241     sequence_raw = "";
242
243     while ( !data_file.eof() && read_seq ) {
244       multiplatform_getline(data_file,file_data_line);
245       ++line_counter;
246       if (file_data_line.substr(0,1) == ">")
247         read_seq = false;
248       else {
249         for (std::string::const_iterator line_i = file_data_line.begin();
250              line_i != file_data_line.end();
251              ++line_i)
252          {
253            if(alpha.exists(*line_i)) {
254              sequence_raw += *line_i;
255            } else {
256             std::ostringstream msg;
257             msg << "Unrecognized characters in fasta sequence at line ";
258             msg << line_counter;
259             throw sequence_invalid_load_error(msg.str());
260            }
261          }
262       }
263     }
264
265     // Lastly, if subselection of the sequence was specified we keep cut out
266     // and only keep that part
267     // end_index = 0 means no end was specified, so cut to the end 
268     if (end_index == 0)
269       end_index = sequence_raw.size();
270
271     // sequence filtering for upcasing agctn and convert non AGCTN to N
272     if (end_index-start_index <= 0) {
273       std::string msg("The selected sequence appears to be empty"); 
274       throw sequence_empty_error(msg);
275     }
276     set_filtered_sequence(sequence_raw, a, start_index, end_index-start_index);
277   } else {
278     std::string errormsg("There were no fasta sequences");
279     throw sequence_empty_file_error(errormsg);
280   }
281 }
282
283 void Sequence::set_filtered_sequence(const std::string &in_seq,
284                                      AlphabetRef alphabet_,
285                                      size_type start,
286                                      size_type count,
287                                      strand_type strand_)
288 {
289   if ( count == npos)
290     count = in_seq.size() - start;
291   std::string new_seq;
292   new_seq.reserve(count);
293
294   // finally, the actual conversion loop
295   const Alphabet& alpha_impl = Alphabet::get_alphabet(alphabet_); // go get one of our actual alphabets
296   std::string::const_iterator seq_i = in_seq.begin()+start;
297   for(size_type i = 0; i != count; ++i, ++seq_i)
298   {
299     if (alpha_impl.exists(*seq_i)) {
300       new_seq.append(1, toupper(*seq_i));
301     } else {
302       new_seq.append(1, 'N');
303     }
304   }
305   SeqSpanRef new_seq_ref(new SeqSpan(new_seq, alphabet_)); 
306   seq = new_seq_ref;
307   strand = strand_;
308 }
309
310 void
311 Sequence::load_annot(fs::path file_path, int start_index, int end_index)
312 {
313   if (not fs::exists(file_path)) {
314     throw mussa_load_error("Annotation File " + file_path.string() + " was not found");
315   }
316   if (fs::is_directory(file_path)) {
317     throw mussa_load_error(file_path.string() +
318             " is a directory, please provide a file for annotations."
319           );
320   }    
321   fs::fstream data_stream(file_path, std::ios::in);
322   if (!data_stream)
323   {
324     throw mussa_load_error("Error loading annotation file " + file_path.string());
325   }
326
327   // so i should probably be passing the parse function some iterators
328   // but the annotations files are (currently) small, so i think i can 
329   // get away with loading the whole file into memory
330   std::string data;
331   char c;
332   while(data_stream.good()) {
333     data_stream.get(c);
334     data.push_back(c);
335   }
336   data_stream.close();
337
338   try {  
339     parse_annot(data, start_index, end_index);
340   } catch(annotation_load_error e) {
341     std::ostringstream msg;
342     msg << file_path.native_file_string()
343         << " "
344         << e.what();
345     throw annotation_load_error(msg.str());
346   }
347 }
348
349 /* If this works, yikes, this is some brain hurting code.
350  *
351  * what's going on is that when pb_annot is instantiated it stores references
352  * to begin, end, name, type, declared in the parse function, then
353  * when operator() is called it grabs values from those references
354  * and uses that to instantiate an annot object and append that to our
355  * annotation list.
356  *
357  * This weirdness is because the spirit library requires that actions
358  * conform to a specific prototype operator()(IteratorT, IteratorT)
359  * which doesn't provide any useful opportunity for me to actually
360  * grab the results of our parsing.
361  *
362  * so I instantiate this structure in order to have a place to grab
363  * my data from.
364  */
365   
366 struct push_back_annot {
367   std::list<annot>& annot_list;
368   int& begin;
369   int& end;
370   std::string& name;
371   std::string& type;
372   int &parsed;
373
374   push_back_annot(std::list<annot>& annot_list_, 
375                   int& begin_, 
376                   int& end_, 
377                   std::string& name_, 
378                   std::string& type_,
379                   int &parsed_) 
380   : annot_list(annot_list_), 
381     begin(begin_),
382     end(end_),
383     name(name_),
384     type(type_),
385     parsed(parsed_)
386   {
387   }
388
389   void operator()(std::string::const_iterator, 
390                   std::string::const_iterator) const 
391   {
392     //std::cout << "adding annot: " << begin << "|" << end << "|" << name << "|" << type << std::endl;
393     annot_list.push_back(annot(begin, end, name, type));
394     ++parsed;
395   };
396 };
397
398 struct push_back_seq {
399   std::list<Sequence>& seq_list;
400   std::string& name;
401   std::string& seq;
402   int &parsed;
403
404   push_back_seq(std::list<Sequence>& seq_list_,
405                 std::string& name_, 
406                 std::string& seq_,
407                 int &parsed_)
408   : seq_list(seq_list_), 
409     name(name_),
410     seq(seq_),
411     parsed(parsed_)
412   {
413   }
414
415   void operator()(std::string::const_iterator, 
416                   std::string::const_iterator) const 
417   {
418     // filter out newlines from our sequence
419     std::string new_seq;
420     for(std::string::const_iterator seq_i = seq.begin();
421         seq_i != seq.end();
422         ++seq_i)
423     {
424       if (*seq_i != '\015' && *seq_i != '\012') new_seq += *seq_i;
425     }
426     //std::cout << "adding seq: " << name << " " << new_seq << std::endl;
427     
428     Sequence s(new_seq);
429     s.set_fasta_header(name);
430     seq_list.push_back(s);
431     ++parsed;
432   };
433 };
434
435 void
436 Sequence::parse_annot(std::string data, int start_index, int end_index)
437 {
438   int start=0;
439   int end=0;
440   std::string name;
441   std::string type;
442   std::string seq;
443   std::list<annot> parsed_annots;
444   std::list<Sequence> query_seqs;
445   int parsed=0;
446
447   bool ok = spirit::parse(data.begin(), data.end(),
448               (
449                //begin grammar
450                  !(
451                     (
452                       spirit::alpha_p >> 
453                       +(spirit::graph_p)
454                     )[spirit::assign_a(species)] >> 
455                     +(spirit::space_p)
456                   ) >>
457                   *(
458                      ( // ignore html tags
459                        *(spirit::space_p) >>
460                        spirit::ch_p('<') >> 
461                        +(~spirit::ch_p('>')) >>
462                        spirit::ch_p('>') >>
463                        *(spirit::space_p)
464                      )
465                    |
466                     ( // parse an absolute location name
467                      (spirit::uint_p[spirit::assign_a(start)] >> 
468                       +spirit::space_p >>
469                       spirit::uint_p[spirit::assign_a(end)] >> 
470                       +spirit::space_p >>
471                       ( 
472                          spirit::alpha_p >> 
473                          *spirit::graph_p
474                       )[spirit::assign_a(name)] >> 
475                       // optional type
476                       !(
477                           +spirit::space_p >>
478                           (
479                             spirit::alpha_p >>
480                             *spirit::graph_p
481                           )[spirit::assign_a(type)]
482                       )
483                       // to understand how this group gets set
484                       // read the comment above struct push_back_annot
485                      )[push_back_annot(parsed_annots, start, end, type, name, parsed)]
486                    |
487                     ((spirit::ch_p('>')|spirit::str_p("&gt;")) >> 
488                        (*(spirit::print_p))[spirit::assign_a(name)] >>
489                        spirit::eol_p >> 
490                        (+(spirit::chset<>(Alphabet::nucleic_cstr)))[spirit::assign_a(seq)]
491                      )[push_back_seq(query_seqs, name, seq, parsed)]
492                     ) >>
493                     *spirit::space_p
494                    )
495               //end grammar
496               )).full;
497   if (not ok) {
498     std::stringstream msg;
499     msg << "Error parsing annotation #" << parsed;
500     throw annotation_load_error(msg.str());
501   }
502   // add newly parsed annotations to our sequence
503   std::copy(parsed_annots.begin(), parsed_annots.end(), std::back_inserter(annots));
504   // go seearch for query sequences 
505   find_sequences(query_seqs.begin(), query_seqs.end());
506 }
507
508 void Sequence::add_annotation(const annot& a)
509 {
510   annots.push_back(a);
511 }
512
513 const std::list<annot>& Sequence::annotations() const
514 {
515   return annots;
516 }
517
518 void Sequence::copy_children(Sequence &new_seq, size_type start, size_type count) const
519 {
520   new_seq.motif_list = motif_list;
521   new_seq.annots.clear();
522
523   for(std::list<annot>::const_iterator annot_i = annots.begin();
524       annot_i != annots.end();
525       ++annot_i)
526   {
527     size_type annot_begin= annot_i->begin;
528     size_type annot_end = annot_i->end;
529
530     if (annot_begin < start+count) {
531       if (annot_begin >= start) {
532         annot_begin -= start;
533       } else {
534         annot_begin = 0;
535       }
536
537       if (annot_end < start+count) {
538         annot_end -= start;
539       } else {
540         annot_end = count;
541       }
542
543       annot new_annot(annot_begin, annot_end, annot_i->type, annot_i->name);
544       new_seq.annots.push_back(new_annot);
545     }
546   }
547   
548 }
549 Sequence
550 Sequence::subseq(size_type start, size_type count) const
551 {
552   if (!seq) {
553     Sequence new_seq;
554     return new_seq;
555   }
556
557   Sequence new_seq = *this;
558   new_seq.seq = seq->subseq(start, count);
559
560   copy_children(new_seq, start, count);
561   
562   return new_seq;
563 }
564
565
566 // FIXME: This needs to be moved into SeqSpan
567 Sequence Sequence::rev_comp() const
568 {
569   std::string rev_comp;
570   rev_comp.reserve(length());
571   
572   std::string rc_map = seq->seq->create_reverse_complement_map();
573
574   // reverse and convert
575   Sequence::const_reverse_iterator seq_i;
576   Sequence::const_reverse_iterator seq_end = rend();  
577   for(seq_i = rbegin(); 
578       seq_i != seq_end;
579       ++seq_i)
580   {
581     rev_comp.append(1, rc_map[*seq_i]);
582   }
583   return Sequence(rev_comp, seq->seq->get_alphabet_ref()); 
584 }
585
586 const Alphabet& Sequence::get_alphabet() const
587 {
588   return (seq) ? seq->get_alphabet() : Alphabet::empty_alphabet(); 
589 }
590
591 void Sequence::set_fasta_header(std::string header_)
592 {
593   header = header_;
594 }
595
596 void Sequence::set_species(const std::string& name)
597 {
598   species = name;
599 }
600
601 std::string Sequence::get_species() const
602 {
603   return species;
604 }
605
606
607 std::string
608 Sequence::get_fasta_header() const
609 {
610   return header;
611 }
612
613 std::string
614 Sequence::get_name() const
615 {
616   if (header.size() > 0) 
617     return header;
618   else if (species.size() > 0)
619     return species;
620   else
621     return "";
622 }
623
624 void Sequence::set_sequence(const std::string& s, AlphabetRef a) 
625 {
626   set_filtered_sequence(s, a);
627 }
628
629 std::string Sequence::get_sequence() const
630 {
631   return seq->sequence();
632 }
633
634 Sequence::const_reference Sequence::operator[](Sequence::size_type i) const
635 {
636   return at(i);
637 }
638
639 void
640 Sequence::clear()
641 {
642   seq.reset();
643   strand = UnknownStrand;
644   header.clear();
645   species.clear();
646   annots.clear();
647   motif_list.clear();
648 }
649
650 void
651 Sequence::save(fs::fstream &save_file)
652 {
653   //fstream save_file;
654   std::list<annot>::iterator annots_i;
655
656   // not sure why, or if i'm doing something wrong, but can't seem to pass
657   // file pointers down to this method from the mussa control class
658   // so each call to save a sequence appends to the file started by mussa_class
659   //save_file.open(save_file_path.c_str(), std::ios::app);
660
661   save_file << "<Sequence>" << std::endl;
662   save_file << *this << std::endl;
663   save_file << "</Sequence>" << std::endl;
664
665   save_file << "<Annotations>" << std::endl;
666   save_file << species << std::endl;
667   for (annots_i = annots.begin(); annots_i != annots.end(); ++annots_i)
668   {
669     save_file << annots_i->begin << " " << annots_i->end << " " ;
670     save_file << annots_i->name << " " << annots_i->type << std::endl;
671   }
672   save_file << "</Annotations>" << std::endl;
673   //save_file.close();
674 }
675
676 void
677 Sequence::load_museq(fs::path load_file_path, int seq_num)
678 {
679   fs::fstream load_file;
680   std::string file_data_line;
681   int seq_counter;
682   annot an_annot;
683   std::string::size_type space_split_i;
684   std::string annot_value;
685
686   annots.clear();
687   load_file.open(load_file_path, std::ios::in);
688
689   seq_counter = 0;
690   // search for the seq_num-th sequence 
691   while ( (!load_file.eof()) && (seq_counter < seq_num) )
692   {
693     getline(load_file,file_data_line);
694     if (file_data_line == "<Sequence>")
695       seq_counter++;
696   }
697   getline(load_file, file_data_line);
698   // looks like the sequence is written as a single line
699   set_filtered_sequence(file_data_line, reduced_dna_alphabet);
700   getline(load_file, file_data_line);
701   getline(load_file, file_data_line);
702   if (file_data_line == "<Annotations>")
703   {
704     getline(load_file, file_data_line);
705     species = file_data_line;
706     while ( (!load_file.eof())  && (file_data_line != "</Annotations>") )
707     {
708       getline(load_file,file_data_line);
709       if ((file_data_line != "") && (file_data_line != "</Annotations>"))  
710       {
711         // need to get 4 values...almost same code 4 times...
712         // get annot start index
713         space_split_i = file_data_line.find(" ");
714         annot_value = file_data_line.substr(0,space_split_i);
715         an_annot.begin = atoi (annot_value.c_str());
716         file_data_line = file_data_line.substr(space_split_i+1);
717         // get annot end index
718         space_split_i = file_data_line.find(" ");
719         annot_value = file_data_line.substr(0,space_split_i);
720         an_annot.end = atoi (annot_value.c_str());
721
722         if (space_split_i == std::string::npos)  // no entry for type or name
723         {
724           std::cout << "seq, annots - no type or name\n";
725           an_annot.type = "";
726           an_annot.name = "";
727         }
728         else   // else get annot type
729         {
730           file_data_line = file_data_line.substr(space_split_i+1);
731           space_split_i = file_data_line.find(" ");
732           annot_value = file_data_line.substr(0,space_split_i);
733           an_annot.type = annot_value;
734           if (space_split_i == std::string::npos)  // no entry for name
735           {
736             std::cout << "seq, annots - no name\n";
737             an_annot.name = "";
738           }
739           else          // get annot name
740           {
741             file_data_line = file_data_line.substr(space_split_i+1);
742             space_split_i = file_data_line.find(" ");
743             annot_value = file_data_line.substr(0,space_split_i);
744             an_annot.type = annot_value;
745           }
746         }
747         annots.push_back(an_annot);  // don't forget to actually add the annot
748       }
749       //std::cout << "seq, annots: " << an_annot.start << ", " << an_annot.end
750       //     << "-->" << an_annot.type << "::" << an_annot.name << std::endl;
751     }
752   }
753   load_file.close();
754 }
755
756
757 void Sequence::add_motif(const Sequence& a_motif)
758 {
759   std::vector<int> motif_starts = find_motif(a_motif);
760
761   for(std::vector<int>::iterator motif_start_i = motif_starts.begin();
762       motif_start_i != motif_starts.end();
763       ++motif_start_i)
764   {
765     motif_list.push_back(motif(*motif_start_i, a_motif.get_sequence()));
766   }
767 }
768
769 void Sequence::clear_motifs()
770 {
771   motif_list.clear();
772 }
773
774 const std::list<motif>& Sequence::motifs() const
775 {
776   return motif_list;
777 }
778
779 std::vector<int>
780 Sequence::find_motif(const Sequence& a_motif) const
781 {
782   std::vector<int> motif_match_starts;
783   Sequence norm_motif_rc;
784
785   motif_match_starts.clear();
786   // std::cout << "motif is: " << norm_motif << std::endl;
787
788   if (a_motif.size() > 0)
789   {
790     //std::cout << "Sequence: none blank motif\n";
791     motif_scan(a_motif, &motif_match_starts);
792
793     norm_motif_rc = a_motif.rev_comp();;
794     // make sure not to do search again if it is a palindrome
795     if (norm_motif_rc != a_motif) {
796       motif_scan(norm_motif_rc, &motif_match_starts);
797     }
798   }
799   return motif_match_starts;
800 }
801
802 void
803 Sequence::motif_scan(const Sequence& a_motif, std::vector<int> * motif_match_starts) const
804 {
805   // if there's no sequence we can't scan for it?
806   // should this throw an exception?
807   if (!seq) return;
808
809   std::string::size_type seq_i = 0;
810   Sequence::size_type motif_i = 0;
811   Sequence::size_type motif_len = a_motif.length();
812   Sequence::value_type motif_char;
813   Sequence::value_type seq_char;
814
815   while (seq_i < size())
816   {
817     // this is pretty much a straight translation of Nora's python code
818     // to match iupac letter codes
819     motif_char = toupper(a_motif[motif_i]);
820     seq_char = toupper(seq->at(seq_i));
821     if (motif_char =='N')
822       motif_i++;
823     else if (motif_char == seq_char)
824       motif_i++;
825     else if ((motif_char =='M') && ((seq_char=='A') || (seq_char=='C')))
826       motif_i++;
827     else if ((motif_char =='R') && ((seq_char=='A') || (seq_char=='G')))
828       motif_i++;
829     else if ((motif_char =='W') && ((seq_char=='A') || (seq_char=='T')))
830       motif_i++;
831     else if ((motif_char =='S') && ((seq_char=='C') || (seq_char=='G')))
832       motif_i++;
833     else if ((motif_char =='Y') && ((seq_char=='C') || (seq_char=='T')))
834       motif_i++;
835     else if ((motif_char =='K') && ((seq_char=='G') || (seq_char=='T')))
836       motif_i++;
837     else if ((motif_char =='V') && 
838              ((seq_char=='A') || (seq_char=='C') || (seq_char=='G')))
839       motif_i++;
840     else if ((motif_char =='H') && 
841              ((seq_char=='A') || (seq_char=='C') || (seq_char=='T')))
842       motif_i++;
843     else if ((motif_char =='D') &&
844              ((seq_char=='A') || (seq_char=='G') || (seq_char=='T')))
845       motif_i++;
846     else if ((motif_char =='B') &&
847              ((seq_char=='C') || (seq_char=='G') || (seq_char=='T')))
848       motif_i++;
849     else
850     {
851       // if a motif doesn't match, erase our current trial and try again
852       seq_i -= motif_i;
853       motif_i = 0;
854     }
855
856     // end Nora stuff, now we see if a match is found this pass
857     if (motif_i == motif_len)
858     {
859       annot new_motif;
860       motif_match_starts->push_back(seq_i - motif_len + 1);
861       motif_i = 0;
862     }
863
864     seq_i++;
865   }
866   //std::cout << std::endl;
867 }
868
869 void Sequence::add_string_annotation(std::string a_seq, 
870                                      std::string name)
871 {
872   std::vector<int> seq_starts = find_motif(a_seq);
873   
874   //std::cout << "searching for " << a_seq << " found " << seq_starts.size() << std::endl;
875
876   for(std::vector<int>::iterator seq_start_i = seq_starts.begin();
877       seq_start_i != seq_starts.end();
878       ++seq_start_i)
879   {
880     annots.push_back(annot(*seq_start_i, 
881                            *seq_start_i+a_seq.size(),
882                            "",
883                            name));
884   }
885 }
886
887 void Sequence::find_sequences(std::list<Sequence>::iterator start, 
888                               std::list<Sequence>::iterator end)
889 {
890   while (start != end) {
891     add_string_annotation(start->get_sequence(), start->get_fasta_header());
892     ++start;
893   }
894 }
895
896
897 std::ostream& operator<<(std::ostream& out, const Sequence& s)
898 {
899   if (s.seq) {
900     for(Sequence::const_iterator s_i = s.begin(); s_i != s.end(); ++s_i) {
901       out << *s_i;
902     }
903   }
904   return out;
905 }
906
907 bool operator<(const Sequence& x, const Sequence& y)
908 {
909   Sequence::const_iterator x_i = x.begin();
910   Sequence::const_iterator y_i = y.begin();
911   // for sequences there's some computation associated with computing .end
912   // so lets cache it.
913   Sequence::const_iterator xend = x.end();
914   Sequence::const_iterator yend = y.end();
915   while(1) {
916     if( x_i == xend and y_i == yend ) {
917       return false;
918     } else if ( x_i == xend ) {
919       return true;
920     } else if ( y_i == yend ) {
921       return false;
922     } else if ( (*x_i) < (*y_i)) {
923       return true;
924     } else if ( (*x_i) > (*y_i) ) {
925       return false;
926     } else {
927       ++x_i;
928       ++y_i;
929     }
930   }
931 }
932
933 template <typename Iter1, typename Iter2>
934 static
935 bool sequence_insensitive_equality(Iter1 abegin, Iter1 aend, Iter2 bbegin, Iter2 bend)
936 {
937   Iter1 aseq_i = abegin;
938   Iter2 bseq_i = bbegin;
939   if (aend-abegin == bend-bbegin) {
940     // since the length of the two sequences is equal, we only need to
941     // test one.
942     for(; aseq_i != aend; ++aseq_i, ++bseq_i) {
943       if (toupper(*aseq_i) != toupper(*bseq_i)) {
944         return false;
945       }
946     }
947     return true;
948   } else {
949     return false;
950   }
951 }
952
953 bool operator==(const Sequence& x, const Sequence& y) 
954 {
955   if (x.seq and y.seq) {
956     // both x and y are defined
957     if (SeqSpan::isFamily(x.seq, y.seq)) {
958       // both are part of the same SeqSpan tree
959       return *(x.seq) == *(y.seq);
960     } else {
961       // we'll have to do a real comparison
962       return sequence_insensitive_equality<SeqSpan::const_iterator, SeqSpan::const_iterator>(
963                x.begin(), x.end(),
964                y.begin(), y.end()
965              );
966     }
967   } else {
968     // true if they're both empty (with either a null SeqSpanRef or 
969     // a zero length string
970     return (x.size() == y.size());
971   }
972 }
973
974 bool operator!=(const Sequence& x, const Sequence& y) 
975 {
976   return not operator==(x, y);
977 }
978