partial implementation of sequence track copy
[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 namespace fs = boost::filesystem;
26
27 #include <boost/spirit/core.hpp>
28 #include <boost/spirit/actor/push_back_actor.hpp>
29 #include <boost/spirit/iterator/file_iterator.hpp>
30 #include <boost/spirit/utility/chset.hpp>
31 namespace spirit = boost::spirit;
32
33 #include "alg/sequence.hpp"
34 #include "mussa_exceptions.hpp"
35
36 #include <string>
37 #include <iostream>
38 #include <sstream>
39
40 // some standard dna alphabets 
41 // \012 = nl
42 // \015 = cr
43 // this should make our sequence parsing end-of-line convention 
44 // independent
45 static const char* dna_alphabet = "AaCcGgTtNn\012\015";
46 static const char* rna_alphabet = "AaCcGgNnUu\012\015";
47 static const char* iupac_alphabet = "AaCcGgTtUuRrYyMmKkSsWwBbDdHhVvNn\012\015";
48
49 annot::annot() 
50  : start(0),
51    end(0),
52    type(""),
53    name("")
54 {
55 }
56
57 annot::annot(int start, int end, std::string type, std::string name)
58  : start(start),
59    end(end),
60    type(type),
61    name(name)
62 {
63 }
64
65 motif::motif(int start, std::string motif)
66  : annot(start, start+motif.size(), "motif", motif),
67    sequence(motif)
68 {
69 }
70
71 Sequence::Sequence()
72  :  sequence(""),
73     header(""),
74     species("")
75 {
76   annots.clear();
77   motif_list.clear();
78 }
79
80 Sequence::Sequence(std::string seq) 
81  :  header(""),
82     species("")
83 {
84   set_filtered_sequence(seq);
85 }
86
87 Sequence &Sequence::operator=(const Sequence& s)
88 {
89   if (this != &s) {
90     sequence = s.sequence;
91     header = s.header;
92     species = s.species;
93     annots = s.annots;
94   }
95   return *this;
96 }
97
98 Sequence &Sequence::operator=(const std::string& s)
99 {
100   set_filtered_sequence(s);
101   return *this;
102 }
103
104 char Sequence::operator[](int index) const
105 {
106   return sequence[index];
107 }
108
109 std::ostream& operator<<(std::ostream& out, const Sequence& seq)
110 {
111   out << "Sequence(" << seq.get_seq() << ")";
112   return out;
113 }
114
115 static void multiplatform_getline(std::istream& in, std::string& line)
116 {
117   line.clear();
118   char c;
119   in.get(c);
120   while(in.good() and !(c == '\012' or c == '\015') ) {
121     line.push_back(c);
122     in.get(c);
123   }
124   // if we have cr-lf eat it
125   c = in.peek();
126   if (c=='\012' or c == '\015') {
127     in.get();
128   }
129  
130 }
131
132 //! load a fasta file into a sequence
133 /*! 
134  * \param file_path the location of the fasta file in the filesystem
135  * \param seq_num which sequence in the file to load
136  * \param start_index starting position in the fasta sequence, 0 for beginning
137  * \param end_index ending position in the fasta sequence, 0 for end
138  * \return error message, empty string if no error. (gag!)
139  */
140 void Sequence::load_fasta(fs::path file_path, int seq_num,
141                           int start_index, int end_index)
142 {
143   fs::fstream data_file;
144   data_file.open(file_path, std::ios::in);
145
146   if (!data_file.good())
147   {    
148     throw mussa_load_error("Sequence File: "+file_path.string()+" not found"); 
149   } else {
150     try {
151       load_fasta(data_file, seq_num, start_index, end_index);
152     } catch(sequence_empty_error e) {
153       // there doesn't appear to be any sequence
154       // catch and rethrow to include the filename
155       std::stringstream msg;
156       msg << "The selected sequence in " 
157           << file_path.native_file_string()
158           << " appears to be empty"; 
159       throw sequence_empty_error(msg.str());
160     } catch(sequence_empty_file_error e) {
161       std::stringstream errormsg;
162       errormsg << file_path.native_file_string()
163                << " did not have any fasta sequences" << std::endl;
164       throw sequence_empty_file_error(errormsg.str());
165     }
166   }
167 }
168
169 void
170 Sequence::load_fasta(std::iostream& data_file, int seq_num, 
171                      int start_index, int end_index)
172 {
173   std::string file_data_line;
174   int header_counter = 0;
175   bool read_seq = true;
176   std::string rev_comp;
177   std::string sequence_raw;
178   std::string seq_tmp;             // holds sequence during basic filtering
179
180   if (seq_num == 0) {
181     throw mussa_load_error("fasta sequence number is 1 based (can't be 0)");
182   }
183
184   // search for the header of the fasta sequence we want
185   while ( (!data_file.eof()) && (header_counter < seq_num) )
186   {
187     multiplatform_getline(data_file, file_data_line);
188     if (file_data_line.substr(0,1) == ">")
189       header_counter++;
190   }
191
192   if (header_counter > 0) {
193     header = file_data_line.substr(1);
194
195     sequence_raw = "";
196
197     while ( !data_file.eof() && read_seq ) {
198       multiplatform_getline(data_file,file_data_line);
199       if (file_data_line.substr(0,1) == ">")
200         read_seq = false;
201       else sequence_raw += file_data_line;
202     }
203
204     // Lastly, if subselection of the sequence was specified we keep cut out
205     // and only keep that part
206     // end_index = 0 means no end was specified, so cut to the end 
207     if (end_index == 0)
208       end_index = sequence_raw.size();
209
210     // sequence filtering for upcasing agctn and convert non AGCTN to N
211     if (end_index-start_index <= 0) {
212       std::string msg("The selected sequence appears to be empty"); 
213       throw sequence_empty_error(msg);
214     }
215     set_filtered_sequence(sequence_raw, start_index, end_index-start_index);
216   } else {
217     std::string errormsg("There were no fasta sequences");
218     throw sequence_empty_file_error(errormsg);
219   }
220 }
221
222 void Sequence::set_filtered_sequence(const std::string &old_seq, 
223                                      std::string::size_type start, 
224                                      std::string::size_type count)
225 {
226   char conversionTable[257];
227
228   if ( count == 0)
229     count = old_seq.size() - start;
230   sequence.clear();
231   sequence.reserve(count);
232
233   // Make a conversion table
234
235   // everything we don't specify below will become 'N'
236   for(int table_i=0; table_i < 256; table_i++)
237   {
238     conversionTable[table_i] = 'N';
239   }
240   // add end of string character for printing out table for testing purposes
241   conversionTable[256] = '\0';
242
243   // we want these to map to themselves - ie not to change
244   conversionTable[(int)'A'] = 'A';
245   conversionTable[(int)'T'] = 'T';
246   conversionTable[(int)'G'] = 'G';
247   conversionTable[(int)'C'] = 'C';
248   // this is to upcase
249   conversionTable[(int)'a'] = 'A';
250   conversionTable[(int)'t'] = 'T';
251   conversionTable[(int)'g'] = 'G';
252   conversionTable[(int)'c'] = 'C';
253
254   // finally, the actual conversion loop
255   for(std::string::size_type seq_index = 0; seq_index < count; seq_index++)
256   {
257     sequence += conversionTable[ (int)old_seq[seq_index+start]];
258   }
259 }
260
261   // this doesn't work properly under gcc 3.x ... it can't recognize toupper
262   //transform(sequence.begin(), sequence.end(), sequence.begin(), toupper);
263
264 void
265 Sequence::load_annot(fs::path file_path, int start_index, int end_index)
266 {
267   fs::fstream data_stream(file_path, std::ios::in);
268   if (!data_stream)
269   {
270     throw mussa_load_error("Sequence File: " + file_path.string() + " not found");
271   }
272
273   // so i should probably be passing the parse function some iterators
274   // but the annotations files are (currently) small, so i think i can 
275   // get away with loading the whole file into memory
276   std::string data;
277   char c;
278   while(data_stream.good()) {
279     data_stream.get(c);
280     data.push_back(c);
281   }
282   data_stream.close();
283         
284   parse_annot(data, start_index, end_index);
285 }
286
287 /* If this works, yikes, this is some brain hurting code.
288  *
289  * what's going on is that when pb_annot is instantiated it stores references
290  * to begin, end, name, type, declared in the parse function, then
291  * when operator() is called it grabs values from those references
292  * and uses that to instantiate an annot object and append that to our
293  * annotation list.
294  *
295  * This weirdness is because the spirit library requires that actions
296  * conform to a specific prototype operator()(IteratorT, IteratorT)
297  * which doesn't provide any useful opportunity for me to actually
298  * grab the results of our parsing.
299  *
300  * so I instantiate this structure in order to have a place to grab
301  * my data from.
302  */
303   
304 struct push_back_annot {
305   std::list<annot>& annot_list;
306   int& begin;
307   int& end;
308   std::string& name;
309   std::string& type;
310
311   push_back_annot(std::list<annot>& annot_list_, 
312                   int& begin_, 
313                   int& end_, 
314                   std::string& name_, 
315                   std::string& type_) 
316   : annot_list(annot_list_), 
317     begin(begin_),
318     end(end_),
319     name(name_),
320     type(type_)
321   {
322   }
323
324   void operator()(std::string::const_iterator, 
325                   std::string::const_iterator) const 
326   {
327     //std::cout << "adding annot: " << begin << " " << end << " " << name << " " << type << std::endl;
328     annot_list.push_back(annot(begin, end, name, type));
329   };
330 };
331
332 struct push_back_seq {
333   std::list<Sequence>& seq_list;
334   std::string& name;
335   std::string& seq;
336
337   push_back_seq(std::list<Sequence>& seq_list_,
338                 std::string& name_, 
339                 std::string& seq_)
340   : seq_list(seq_list_), 
341     name(name_),
342     seq(seq_)
343   {
344   }
345
346   void operator()(std::string::const_iterator, 
347                   std::string::const_iterator) const 
348   {
349     // filter out newlines from our sequence
350     std::string new_seq;
351     for(std::string::const_iterator seq_i = seq.begin();
352         seq_i != seq.end();
353         ++seq_i)
354     {
355       if (*seq_i != '\015' && *seq_i != '\012') new_seq += *seq_i;
356     }
357     //std::cout << "adding seq: " << name << " " << new_seq << std::endl;
358     
359     Sequence s(new_seq);
360     s.set_header(name);
361     seq_list.push_back(s);
362   };
363 };
364
365 void
366 Sequence::parse_annot(std::string data, int start_index, int end_index)
367 {
368   int start=0;
369   int end=0;
370   std::string name;
371   std::string type;
372   std::string seq;
373   std::list<Sequence> query_seqs;
374
375   bool status = spirit::parse(data.begin(), data.end(),
376                 (
377                 //begin grammar
378                 (+(spirit::alpha_p))[spirit::assign_a(species)] >> 
379                  +(spirit::space_p) >>
380                     *(
381                       ( // parse an absolute location name
382                        (spirit::uint_p[spirit::assign_a(start)] >> 
383                         +spirit::space_p >>
384                         spirit::uint_p[spirit::assign_a(end)] >> 
385                         +spirit::space_p >>
386                         (*(spirit::alpha_p|spirit::digit_p))[spirit::assign_a(name)] >> 
387                           // optional type
388                           !(
389                              +spirit::space_p >>
390                              (*(spirit::alpha_p))[spirit::assign_a(type)]
391                            )
392                         // to understand how this group gets set
393                         // read the comment above struct push_back_annot
394                        )[push_back_annot(annots, start, end, type, name)]
395                      |
396                       (spirit::ch_p('>') >> 
397                          (*(spirit::print_p))[spirit::assign_a(name)] >>
398                          spirit::eol_p >> 
399                          (+(spirit::chset<>(iupac_alphabet)))[spirit::assign_a(seq)]
400                        )[push_back_seq(query_seqs, name, seq)]
401                       ) >>
402                       *spirit::space_p
403                      )
404                 //end grammar
405                 ) /*,
406                 spirit::space_p*/).full;
407                 
408   // go seearch for query sequences 
409   find_sequences(query_seqs.begin(), query_seqs.end());
410 }
411
412 /*
413 void
414 Sequence::load_annot(std::istream& data_stream, int start_index, int end_index)
415 {
416   std::string file_data_line;
417   annot an_annot;
418   std::string::size_type space_split_i;
419   std::string annot_value;
420   std::list<annot>::iterator list_i;
421   std::string err_msg;
422
423   annots.clear();
424
425   getline(data_stream,file_data_line);
426   species = file_data_line;
427
428   // end_index = 0 means no end was specified, so cut to the end 
429   if (end_index == 0)
430     end_index = sequence.length();
431
432   //std::cout << "START: " << start_index << " END: " << end_index << std::endl;
433
434   while ( !data_stream.eof() )
435   {
436     getline(data_stream,file_data_line);
437     if (file_data_line != "")
438     {
439       // need to get 4 values...almost same code 4 times...
440       // get annot start index
441       space_split_i = file_data_line.find(" ");
442       annot_value = file_data_line.substr(0,space_split_i);
443       an_annot.start = atoi (annot_value.c_str());
444       file_data_line = file_data_line.substr(space_split_i+1);
445       // get annot end index
446       space_split_i = file_data_line.find(" ");
447       annot_value = file_data_line.substr(0,space_split_i);
448       an_annot.end = atoi (annot_value.c_str());
449       file_data_line = file_data_line.substr(space_split_i+1);
450
451       //std::cout << "seq, annots: " << an_annot.start << ", " << an_annot.end
452       //     << std::endl;
453
454       // get annot name
455       space_split_i = file_data_line.find(" ");
456       if (space_split_i == std::string::npos)  // no entries for name & type
457       {
458         std::cout << "seq, annots - no name or type\n";
459         an_annot.name = "";
460         an_annot.type = "";
461       }
462       else
463       {
464         annot_value = file_data_line.substr(0,space_split_i);
465         an_annot.name = annot_value;
466         file_data_line = file_data_line.substr(space_split_i+1);
467         // get annot type
468         space_split_i = file_data_line.find(" ");
469         if (space_split_i == std::string::npos)  // no entry for type
470           an_annot.type = "";
471         else
472         {
473           annot_value = file_data_line.substr(0,space_split_i);
474           an_annot.type = annot_value;
475         }
476       }
477
478
479       // add annot to list if it falls within the range of sequence specified
480       if ((start_index <= an_annot.start) && (end_index >= an_annot.end)) 
481       {
482         an_annot.start -= start_index;
483         an_annot.end -= start_index;
484         annots.push_back(an_annot);
485       }
486       // else no (or bogus) annotations
487     }
488   }
489 }
490 */
491
492 const std::string& Sequence::get_species() const
493 {
494   return species;
495 }
496
497 bool Sequence::empty() const
498 {
499   return (size() == 0);
500 }
501
502 const std::list<annot>& Sequence::annotations() const
503 {
504   return annots;
505 }
506
507 std::string::size_type Sequence::length() const
508 {
509   return size();
510 }
511
512 std::string::size_type Sequence::size() const
513 {
514   return sequence.size();
515 }
516
517 Sequence::iterator Sequence::begin()
518 {
519   return sequence.begin();
520 }
521
522 Sequence::const_iterator Sequence::begin() const
523 {
524   return sequence.begin();
525 }
526
527 Sequence::iterator Sequence::end()
528 {
529   return sequence.end();
530 }
531
532 Sequence::const_iterator Sequence::end() const
533 {
534   return sequence.end();
535 }
536
537
538 const std::string&
539 Sequence::get_seq() const
540 {
541   return sequence;
542 }
543
544
545 std::string
546 Sequence::subseq(int start, int count) const
547 {
548   return sequence.substr(start, count);
549 }
550
551
552 const char *
553 Sequence::c_seq() const
554 {
555   return sequence.c_str();
556 }
557
558 std::string
559 Sequence::rev_comp() const
560 {
561   std::string rev_comp;
562   char conversionTable[257];
563   int seq_i, table_i, len;
564
565   len = sequence.length();
566   rev_comp.reserve(len);
567   // make a conversion table
568   // init all parts of conversion table to '~' character
569   // '~' I doubt will ever appear in a sequence file (jeez, I hope)
570   // and may the fleas of 1000 camels infest the genitals of any biologist (and
571   // seven generations of their progeny) who decides to make it mean
572   // something special!!!
573   // PS - double the curse for any smartass non-biologist who tries it as well
574   for(table_i=0; table_i < 256; table_i++)
575   {
576     conversionTable[table_i] = '~';
577   }
578   // add end of string character for printing out table for testing purposes
579   conversionTable[256] = '\0';
580
581   // add in the characters for the bases we want to convert
582   conversionTable[(int)'A'] = 'T';
583   conversionTable[(int)'T'] = 'A';
584   conversionTable[(int)'G'] = 'C';
585   conversionTable[(int)'C'] = 'G';
586   conversionTable[(int)'N'] = 'N';
587
588   // finally, the actual conversion loop
589   for(seq_i = len - 1; seq_i >= 0; seq_i--)
590   {
591     table_i = (int) sequence[seq_i];
592     rev_comp += conversionTable[table_i];
593   }
594
595   return rev_comp;
596 }
597
598 void Sequence::set_header(std::string header_)
599 {
600   header = header_;
601 }
602
603 std::string
604 Sequence::get_header() const
605 {
606   return header;
607 }
608 /*
609 //FIXME: i don't think this code is callable
610 std::string 
611 Sequence::sp_name() const
612 {
613   return species;
614 }
615 */
616
617 void
618 Sequence::set_seq(const std::string& a_seq)
619 {
620   set_filtered_sequence(a_seq);
621 }
622
623
624 /*
625 std::string 
626 Sequence::species()
627 {
628   return species;
629 }
630 */
631
632 void
633 Sequence::clear()
634 {
635   sequence = "";
636   header = "";
637   species = "";
638   annots.clear();
639 }
640
641 void
642 Sequence::save(fs::fstream &save_file)
643                //std::string save_file_path)
644 {
645   //fstream save_file;
646   std::list<annot>::iterator annots_i;
647
648   // not sure why, or if i'm doing something wrong, but can't seem to pass
649   // file pointers down to this method from the mussa control class
650   // so each call to save a sequence appends to the file started by mussa_class
651   //save_file.open(save_file_path.c_str(), std::ios::app);
652
653   save_file << "<Sequence>" << std::endl;
654   save_file << sequence << std::endl;
655   save_file << "</Sequence>" << std::endl;
656
657   save_file << "<Annotations>" << std::endl;
658   save_file << species << std::endl;
659   for (annots_i = annots.begin(); annots_i != annots.end(); ++annots_i)
660   {
661     save_file << annots_i->start << " " << annots_i->end << " " ;
662     save_file << annots_i->name << " " << annots_i->type << std::endl;
663   }
664   save_file << "</Annotations>" << std::endl;
665   //save_file.close();
666 }
667
668 void
669 Sequence::load_museq(fs::path load_file_path, int seq_num)
670 {
671   fs::fstream load_file;
672   std::string file_data_line;
673   int seq_counter;
674   annot an_annot;
675   std::string::size_type space_split_i;
676   std::string annot_value;
677
678   annots.clear();
679   load_file.open(load_file_path, std::ios::in);
680
681   seq_counter = 0;
682   // search for the seq_num-th sequence 
683   while ( (!load_file.eof()) && (seq_counter < seq_num) )
684   {
685     getline(load_file,file_data_line);
686     if (file_data_line == "<Sequence>")
687       seq_counter++;
688   }
689   getline(load_file, file_data_line);
690   sequence = file_data_line;
691   getline(load_file, file_data_line);
692   getline(load_file, file_data_line);
693   if (file_data_line == "<Annotations>")
694   {
695     getline(load_file, file_data_line);
696     species = file_data_line;
697     while ( (!load_file.eof())  && (file_data_line != "</Annotations>") )
698     {
699       getline(load_file,file_data_line);
700       if ((file_data_line != "") && (file_data_line != "</Annotations>"))  
701       {
702         // need to get 4 values...almost same code 4 times...
703         // get annot start index
704         space_split_i = file_data_line.find(" ");
705         annot_value = file_data_line.substr(0,space_split_i);
706         an_annot.start = atoi (annot_value.c_str());
707         file_data_line = file_data_line.substr(space_split_i+1);
708         // get annot end index
709         space_split_i = file_data_line.find(" ");
710         annot_value = file_data_line.substr(0,space_split_i);
711         an_annot.end = atoi (annot_value.c_str());
712
713         if (space_split_i == std::string::npos)  // no entry for type or name
714         {
715           std::cout << "seq, annots - no type or name\n";
716           an_annot.type = "";
717           an_annot.name = "";
718         }
719         else   // else get annot type
720         {
721           file_data_line = file_data_line.substr(space_split_i+1);
722           space_split_i = file_data_line.find(" ");
723           annot_value = file_data_line.substr(0,space_split_i);
724           an_annot.type = annot_value;
725           if (space_split_i == std::string::npos)  // no entry for name
726           {
727             std::cout << "seq, annots - no name\n";
728             an_annot.name = "";
729           }
730           else          // get annot name
731           {
732             file_data_line = file_data_line.substr(space_split_i+1);
733             space_split_i = file_data_line.find(" ");
734             annot_value = file_data_line.substr(0,space_split_i);
735             an_annot.type = annot_value;
736           }
737         }
738         annots.push_back(an_annot);  // don't forget to actually add the annot
739       }
740       //std::cout << "seq, annots: " << an_annot.start << ", " << an_annot.end
741       //     << "-->" << an_annot.type << "::" << an_annot.name << std::endl;
742     }
743   }
744   load_file.close();
745 }
746
747
748 std::string
749 Sequence::rc_motif(std::string a_motif)
750 {
751   std::string rev_comp;
752   char conversionTable[257];
753   int seq_i, table_i, len;
754
755   len = a_motif.length();
756   rev_comp.reserve(len);
757
758   for(table_i=0; table_i < 256; table_i++)
759   {
760     conversionTable[table_i] = '~';
761   }
762   // add end of std::string character for printing out table for testing purposes
763   conversionTable[256] = '\0';
764
765   // add in the characters for the bases we want to convert (IUPAC)
766   conversionTable[(int)'A'] = 'T';
767   conversionTable[(int)'T'] = 'A';
768   conversionTable[(int)'G'] = 'C';
769   conversionTable[(int)'C'] = 'G';
770   conversionTable[(int)'N'] = 'N';
771   conversionTable[(int)'M'] = 'K';
772   conversionTable[(int)'R'] = 'Y';
773   conversionTable[(int)'W'] = 'W';
774   conversionTable[(int)'S'] = 'S';
775   conversionTable[(int)'Y'] = 'R';
776   conversionTable[(int)'K'] = 'M';
777   conversionTable[(int)'V'] = 'B';
778   conversionTable[(int)'H'] = 'D';
779   conversionTable[(int)'D'] = 'H';
780   conversionTable[(int)'B'] = 'V';
781
782   // finally, the actual conversion loop
783   for(seq_i = len - 1; seq_i >= 0; seq_i--)
784   {
785     //std::cout << "** i = " << seq_i << " bp = " << 
786     table_i = (int) a_motif[seq_i];
787     rev_comp += conversionTable[table_i];
788   }
789
790   //std::cout << "seq: " << a_motif << std::endl;
791   //std::cout << "rc:  " << rev_comp << std::endl;
792
793   return rev_comp;
794 }
795
796 std::string
797 Sequence::motif_normalize(std::string a_motif)
798 {
799   std::string valid_motif;
800   int seq_i, len;
801
802   len = a_motif.length();
803   valid_motif.reserve(len);
804
805   // this just upcases IUPAC symbols.  Eventually should return an error if non IUPAC is present.
806   // current nonIUPAC symbols are omitted, which is not reported atm
807   for(seq_i = 0; seq_i < len; seq_i++)
808   {
809     if ((a_motif[seq_i] == 'a') || (a_motif[seq_i] == 'A'))
810       valid_motif += 'A';
811     else if ((a_motif[seq_i] == 't') || (a_motif[seq_i] == 'T'))
812       valid_motif += 'T';
813     else if ((a_motif[seq_i] == 'g') || (a_motif[seq_i] == 'G'))
814       valid_motif += 'G';
815     else if ((a_motif[seq_i] == 'c') || (a_motif[seq_i] == 'C'))
816       valid_motif += 'C';
817     else if ((a_motif[seq_i] == 'n') || (a_motif[seq_i] == 'N'))
818       valid_motif += 'N';
819     else if ((a_motif[seq_i] == 'm') || (a_motif[seq_i] == 'M'))
820       valid_motif += 'M';
821     else if ((a_motif[seq_i] == 'r') || (a_motif[seq_i] == 'R'))
822       valid_motif += 'R';
823     else if ((a_motif[seq_i] == 'w') || (a_motif[seq_i] == 'W'))
824       valid_motif += 'W';
825     else if ((a_motif[seq_i] == 's') || (a_motif[seq_i] == 'S'))
826       valid_motif += 'S';
827     else if ((a_motif[seq_i] == 'y') || (a_motif[seq_i] == 'Y'))
828       valid_motif += 'Y';
829     else if ((a_motif[seq_i] == 'k') || (a_motif[seq_i] == 'K'))
830       valid_motif += 'G';
831     else if ((a_motif[seq_i] == 'v') || (a_motif[seq_i] == 'V'))
832       valid_motif += 'V';
833     else if ((a_motif[seq_i] == 'h') || (a_motif[seq_i] == 'H'))
834       valid_motif += 'H';
835     else if ((a_motif[seq_i] == 'd') || (a_motif[seq_i] == 'D'))
836       valid_motif += 'D';
837     else if ((a_motif[seq_i] == 'b') || (a_motif[seq_i] == 'B'))
838       valid_motif += 'B';
839     else {
840       std::string msg = "Letter ";
841       msg += a_motif[seq_i];
842       msg += " is not a valid IUPAC symbol";
843       throw motif_normalize_error(msg);
844     }
845   }
846   //std::cout << "valid_motif is: " << valid_motif << std::endl;
847   return valid_motif;
848 }
849
850 void Sequence::add_motif(std::string a_motif)
851 {
852   std::vector<int> motif_starts = find_motif(a_motif);
853
854   for(std::vector<int>::iterator motif_start_i = motif_starts.begin();
855       motif_start_i != motif_starts.end();
856       ++motif_start_i)
857   {
858     motif_list.push_back(motif(*motif_start_i, a_motif));
859   }
860 }
861
862 void Sequence::clear_motifs()
863 {
864   motif_list.clear();
865 }
866
867 const std::list<motif>& Sequence::motifs() const
868 {
869   return motif_list;
870 }
871
872 std::vector<int>
873 Sequence::find_motif(std::string a_motif)
874 {
875   std::vector<int> motif_match_starts;
876   std::string a_motif_rc;
877
878   motif_match_starts.clear();
879
880   //std::cout << "motif is: " << a_motif << std::endl;
881   a_motif = motif_normalize(a_motif);
882   //std::cout << "motif is: " << a_motif << std::endl;
883
884   if (a_motif != "")
885   {
886     //std::cout << "Sequence: none blank motif\n";
887     motif_scan(a_motif, &motif_match_starts);
888
889     a_motif_rc = rc_motif(a_motif);
890     // make sure not to do search again if it is a palindrome
891     if (a_motif_rc != a_motif)
892       motif_scan(a_motif_rc, &motif_match_starts);
893   }
894   return motif_match_starts;
895 }
896
897 void
898 Sequence::motif_scan(std::string a_motif, std::vector<int> * motif_match_starts)
899 {
900   char * seq_c;
901   std::string::size_type seq_i;
902   int motif_i, motif_len;
903
904   // faster to loop thru the sequence as a old c std::string (ie char array)
905   seq_c = (char*)sequence.c_str();
906   //std::cout << "Sequence: motif, seq len = " << sequence.length() << std::endl; 
907   motif_len = a_motif.length();
908
909   //std::cout << "motif_length: " << motif_len << std::endl;
910   //std::cout << "RAAARRRRR\n";
911
912   motif_i = 0;
913
914   //std::cout << "motif: " << a_motif << std::endl;
915
916   //std::cout << "Sequence: motif, length= " << length << std::endl;
917   seq_i = 0;
918   while (seq_i < sequence.length())
919   {
920     //std::cout << seq_c[seq_i];
921     //std::cout << seq_c[seq_i] << "?" << a_motif[motif_i] << ":" << motif_i << " ";
922     // this is pretty much a straight translation of Nora's python code
923     // to match iupac letter codes
924     if (a_motif[motif_i] =='N')
925       motif_i++;
926     else if (a_motif[motif_i] == seq_c[seq_i])
927       motif_i++;
928     else if ((a_motif[motif_i] =='M') && 
929              ((seq_c[seq_i]=='A') || (seq_c[seq_i]=='C')))
930       motif_i++;
931     else if ((a_motif[motif_i] =='R') && 
932              ((seq_c[seq_i]=='A') || (seq_c[seq_i]=='G')))
933       motif_i++;
934     else if ((a_motif[motif_i] =='W') && 
935              ((seq_c[seq_i]=='A') || (seq_c[seq_i]=='T')))
936       motif_i++;
937     else if ((a_motif[motif_i] =='S') && 
938              ((seq_c[seq_i]=='C') || (seq_c[seq_i]=='G')))
939       motif_i++;
940     else if ((a_motif[motif_i] =='Y') && 
941              ((seq_c[seq_i]=='C') || (seq_c[seq_i]=='T')))
942       motif_i++;
943     else if ((a_motif[motif_i] =='K') && 
944              ((seq_c[seq_i]=='G') || (seq_c[seq_i]=='T')))
945       motif_i++;
946     else if ((a_motif[motif_i] =='V') && 
947              ((seq_c[seq_i]=='A') || (seq_c[seq_i]=='C') ||
948               (seq_c[seq_i]=='G')))
949       motif_i++;
950     else if ((a_motif[seq_i] =='H') && 
951              ((seq_c[seq_i]=='A') || (seq_c[seq_i]=='C') ||
952               (seq_c[seq_i]=='T')))
953       motif_i++;
954     else if ((a_motif[motif_i] =='D') &&
955              ((seq_c[seq_i]=='A') || (seq_c[seq_i]=='G') ||
956               (seq_c[seq_i]=='T')))
957       motif_i++;
958     else if ((a_motif[motif_i] =='B') &&
959              ((seq_c[seq_i]=='C') || (seq_c[seq_i]=='G') ||
960               (seq_c[seq_i]=='T')))
961       motif_i++;
962
963     else
964     {
965       seq_i -= motif_i;
966       motif_i = 0;
967     }
968
969     // end Nora stuff, now we see if a match is found this pass
970     if (motif_i == motif_len)
971     {
972       //std::cout << "!!";
973       annot new_motif;
974       motif_match_starts->push_back(seq_i - motif_len + 1);
975       motif_i = 0;
976     }
977
978     seq_i++;
979   }
980   //std::cout << std::endl;
981 }
982
983 void Sequence::add_string_annotation(std::string a_seq, 
984                                      std::string name)
985 {
986   std::vector<int> seq_starts = find_motif(a_seq);
987   
988   //std::cout << "searching for " << a_seq << " found " << seq_starts.size() << std::endl;
989
990   for(std::vector<int>::iterator seq_start_i = seq_starts.begin();
991       seq_start_i != seq_starts.end();
992       ++seq_start_i)
993   {
994     annots.push_back(annot(*seq_start_i, 
995                            *seq_start_i+a_seq.size(),
996                            "",
997                            name));
998   }
999 }
1000
1001 void Sequence::find_sequences(std::list<Sequence>::iterator start, 
1002                               std::list<Sequence>::iterator end)
1003 {
1004   while (start != end) {
1005     add_string_annotation(start->get_seq(), start->get_header());
1006     ++start;
1007   }
1008 }
1009