]> git.donarmstrong.com Git - bamtools.git/blob - src/api/BamAlignment.cpp
Merge pull request #13 from alecchap/master
[bamtools.git] / src / api / BamAlignment.cpp
1 // ***************************************************************************
2 // BamAlignment.cpp (c) 2009 Derek Barnett
3 // Marth Lab, Department of Biology, Boston College
4 // All rights reserved.
5 // ---------------------------------------------------------------------------
6 // Last modified: 22 April 2011 (DB)
7 // ---------------------------------------------------------------------------
8 // Provides the BamAlignment data structure
9 // ***************************************************************************
10
11 #include <api/BamAlignment.h>
12 #include <api/BamConstants.h>
13 using namespace BamTools;
14
15 #include <cctype>
16 #include <cstdio>
17 #include <cstdlib>
18 #include <cstring>
19 #include <exception>
20 #include <iostream>
21 #include <map>
22 #include <utility>
23 using namespace std;
24
25 /*! \class BamTools::BamAlignment
26     \brief The main BAM alignment data structure.
27
28     Provides methods to query/modify BAM alignment data fields.
29 */
30 /*! \var BamAlignment::Name
31     \brief read name
32 */
33 /*! \var BamAlignment::Length
34     \brief length of query sequence
35 */
36 /*! \var BamAlignment::QueryBases
37     \brief 'original' sequence (as reported from sequencing machine)
38 */
39 /*! \var BamAlignment::AlignedBases
40     \brief 'aligned' sequence (includes any indels, padding, clipping)
41 */
42 /*! \var BamAlignment::Qualities
43     \brief FASTQ qualities (ASCII characters, not numeric values)
44 */
45 /*! \var BamAlignment::TagData
46     \brief tag data (use the provided methods to query/modify)
47 */
48 /*! \var BamAlignment::RefID
49     \brief ID number for reference sequence
50 */
51 /*! \var BamAlignment::Position
52     \brief position (0-based) where alignment starts
53 */
54 /*! \var BamAlignment::Bin
55     \brief BAM (standard) index bin number for this alignment
56 */
57 /*! \var BamAlignment::MapQuality
58     \brief mapping quality score
59 */
60 /*! \var BamAlignment::AlignmentFlag
61     \brief alignment bit-flag (use the provided methods to query/modify)
62 */
63 /*! \var BamAlignment::CigarData
64     \brief CIGAR operations for this alignment
65 */
66 /*! \var BamAlignment::MateRefID
67     \brief ID number for reference sequence where alignment's mate was aligned
68 */
69 /*! \var BamAlignment::MatePosition
70     \brief position (0-based) where alignment's mate starts
71 */
72 /*! \var BamAlignment::InsertSize
73     \brief mate-pair insert size
74 */
75 /*! \var BamAlignment::Filename
76     \brief name of BAM file which this alignment comes from
77 */
78
79 /*! \fn BamAlignment::BamAlignment(void)
80     \brief constructor
81 */
82 BamAlignment::BamAlignment(void)
83     : RefID(-1)
84     , Position(-1)
85     , MateRefID(-1)
86     , MatePosition(-1)
87     , InsertSize(0)
88 { }
89
90 /*! \fn BamAlignment::BamAlignment(const BamAlignment& other)
91     \brief copy constructor
92 */
93 BamAlignment::BamAlignment(const BamAlignment& other)
94     : Name(other.Name)
95     , Length(other.Length)
96     , QueryBases(other.QueryBases)
97     , AlignedBases(other.AlignedBases)
98     , Qualities(other.Qualities)
99     , TagData(other.TagData)
100     , RefID(other.RefID)
101     , Position(other.Position)
102     , Bin(other.Bin)
103     , MapQuality(other.MapQuality)
104     , AlignmentFlag(other.AlignmentFlag)
105     , CigarData(other.CigarData)
106     , MateRefID(other.MateRefID)
107     , MatePosition(other.MatePosition)
108     , InsertSize(other.InsertSize)
109     , Filename(other.Filename)
110     , SupportData(other.SupportData)
111 { }
112
113 /*! \fn BamAlignment::~BamAlignment(void)
114     \brief destructor
115 */
116 BamAlignment::~BamAlignment(void) { }
117
118 /*! \fn bool BamAlignment::AddTag(const std::string& tag, const std::string& type, const std::string& value)
119     \brief Adds a field with string data to the BAM tags.
120
121     Does NOT modify an existing tag - use \link BamAlignment::EditTag() \endlink instead.
122
123     \param tag   2-character tag name
124     \param type  1-character tag type (must be "Z" or "H")
125     \param value string data to store
126
127     \return \c true if the \b new tag was added successfully
128     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
129 */
130 bool BamAlignment::AddTag(const std::string& tag, const std::string& type, const std::string& value) {
131   
132     // skip if core data not parsed
133     if ( SupportData.HasCoreOnly ) return false;
134
135     // validate tag/type size & that type is OK for string value
136     if ( !IsValidSize(tag, type) ) return false;
137     if ( type.at(0) != Constants::BAM_TAG_TYPE_STRING &&
138          type.at(0) != Constants::BAM_TAG_TYPE_HEX
139        )
140     {
141         return false;
142     }
143   
144     // localize the tag data
145     char* pTagData = (char*)TagData.data();
146     const unsigned int tagDataLength = TagData.size();
147     unsigned int numBytesParsed = 0;
148     
149     // if tag already exists, return false
150     // use EditTag explicitly instead
151     if ( FindTag(tag, pTagData, tagDataLength, numBytesParsed) )
152         return false;
153   
154     // otherwise, copy tag data to temp buffer
155     string newTag = tag + type + value;
156     const int newTagDataLength = tagDataLength + newTag.size() + 1; // leave room for null-term
157     char* originalTagData = new char[newTagDataLength];
158     memcpy(originalTagData, TagData.c_str(), tagDataLength + 1);    // '+1' for TagData null-term
159
160     // append newTag
161     strcat(originalTagData + tagDataLength, newTag.data());  // removes original null-term, appends newTag + null-term
162
163     // store temp buffer back in TagData
164     const char* newTagData = (const char*)originalTagData;
165     TagData.assign(newTagData, newTagDataLength);
166     
167     delete[] originalTagData;
168
169     // return success
170     return true;
171 }
172
173 /*! \fn bool BamAlignment::AddTag(const std::string& tag, const std::string& type, const uint32_t& value)
174     \brief Adds a field with unsigned integer data to the BAM tags.
175
176     Does NOT modify an existing tag - use \link BamAlignment::EditTag() \endlink instead.
177
178     \param tag   2-character tag name
179     \param type  1-character tag type (must NOT be "f", "Z", "H", or "B")
180     \param value unsigned int data to store
181
182     \return \c true if the \b new tag was added successfully
183     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
184 */
185 bool BamAlignment::AddTag(const std::string& tag, const std::string& type, const uint32_t& value) {
186   
187     // skip if core data not parsed
188     if ( SupportData.HasCoreOnly ) return false;
189
190     // validate tag/type size & that type is OK for uint32_t value
191     if ( !IsValidSize(tag, type) ) return false;
192     if ( type.at(0) == Constants::BAM_TAG_TYPE_FLOAT  ||
193          type.at(0) == Constants::BAM_TAG_TYPE_STRING ||
194          type.at(0) == Constants::BAM_TAG_TYPE_HEX    ||
195          type.at(0) == Constants::BAM_TAG_TYPE_ARRAY
196        )
197     {
198         return false;
199     }
200   
201     // localize the tag data
202     char* pTagData = (char*)TagData.data();
203     const unsigned int tagDataLength = TagData.size();
204     unsigned int numBytesParsed = 0;
205     
206     // if tag already exists, return false
207     // use EditTag explicitly instead
208     if ( FindTag(tag, pTagData, tagDataLength, numBytesParsed) )
209         return false;
210   
211     // otherwise, convert value to string
212     union { uint32_t value; char valueBuffer[sizeof(uint32_t)]; } un;
213     un.value = value;
214
215     // copy original tag data to temp buffer
216     string newTag = tag + type;
217     const int newTagDataLength = tagDataLength + newTag.size() + 4; // leave room for new integer
218     char* originalTagData = new char[newTagDataLength];
219     memcpy(originalTagData, TagData.c_str(), tagDataLength + 1);    // '+1' for TagData null-term
220     
221     // append newTag
222     strcat(originalTagData + tagDataLength, newTag.data());
223     memcpy(originalTagData + tagDataLength + newTag.size(), un.valueBuffer, sizeof(uint32_t));
224     
225     // store temp buffer back in TagData
226     const char* newTagData = (const char*)originalTagData;
227     TagData.assign(newTagData, newTagDataLength);
228     delete[] originalTagData;
229     
230     // return success
231     return true;
232 }
233
234 /*! \fn bool BamAlignment::AddTag(const std::string& tag, const std::string& type, const int32_t& value)
235     \brief Adds a field with signed integer data to the BAM tags.
236
237     Does NOT modify an existing tag - use \link BamAlignment::EditTag() \endlink instead.
238
239     \param tag   2-character tag name
240     \param type  1-character tag type (must NOT be "f", "Z", "H", or "B")
241     \param value signed int data to store
242
243     \return \c true if the \b new tag was added successfully
244     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
245 */
246 bool BamAlignment::AddTag(const std::string& tag, const std::string& type, const int32_t& value) {
247     return AddTag(tag, type, (const uint32_t&)value);
248 }
249
250 /*! \fn bool BamAlignment::AddTag(const std::string& tag, const std::string& type, const float& value)
251     \brief Adds a field with floating-point data to the BAM tags.
252
253     Does NOT modify an existing tag - use \link BamAlignment::EditTag() \endlink instead.
254
255     \param tag  2-character tag name
256     \param type  1-character tag type (must NOT be "Z", "H", or "B")
257     \param value float data to store
258
259     \return \c true if the \b new tag was added successfully
260     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
261 */
262 bool BamAlignment::AddTag(const std::string& tag, const std::string& type, const float& value) {
263   
264     // skip if core data not parsed
265     if ( SupportData.HasCoreOnly ) return false;
266
267     // validate tag/type size & that type is OK for float value
268     if ( !IsValidSize(tag, type) ) return false;
269     if ( type.at(0) == Constants::BAM_TAG_TYPE_STRING ||
270          type.at(0) == Constants::BAM_TAG_TYPE_HEX    ||
271          type.at(0) == Constants::BAM_TAG_TYPE_ARRAY
272        )
273     {
274         return false;
275     }
276
277     // localize the tag data
278     char* pTagData = (char*)TagData.data();
279     const unsigned int tagDataLength = TagData.size();
280     unsigned int numBytesParsed = 0;
281     
282     // if tag already exists, return false
283     // use EditTag explicitly instead
284     if ( FindTag(tag, pTagData, tagDataLength, numBytesParsed) )
285         return false;
286   
287     // otherwise, convert value to string
288     union { float value; char valueBuffer[sizeof(float)]; } un;
289     un.value = value;
290
291     // copy original tag data to temp buffer
292     string newTag = tag + type;
293     const int newTagDataLength = tagDataLength + newTag.size() + 4; // leave room for new float
294     char* originalTagData = new char[newTagDataLength];
295     memcpy(originalTagData, TagData.c_str(), tagDataLength + 1);    // '+1' for TagData null-term
296     
297     // append newTag
298     strcat(originalTagData + tagDataLength, newTag.data());
299     memcpy(originalTagData + tagDataLength + newTag.size(), un.valueBuffer, sizeof(float));
300     
301     // store temp buffer back in TagData
302     const char* newTagData = (const char*)originalTagData;
303     TagData.assign(newTagData, newTagDataLength);
304     
305     delete[] originalTagData;
306
307     // return success
308     return true;
309 }
310
311 /*! \fn bool AddTag(const std::string& tag, const std::vector<uint8_t>& values);
312     \brief Adds a numeric array field to the BAM tags.
313
314     Does NOT modify an existing tag - use \link BamAlignment::EditTag() \endlink instead.
315
316     \param tag    2-character tag name
317     \param values vector of uint8_t values to store
318
319     \return \c true if the \b new tag was added successfully
320     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
321 */
322 bool BamAlignment::AddTag(const std::string& tag, const std::vector<uint8_t>& values) {
323
324     // skip if core data not parsed
325     if ( SupportData.HasCoreOnly ) return false;
326
327     // check for valid tag length
328     if ( tag.size() != Constants::BAM_TAG_TAGSIZE ) return false;
329
330     // localize the tag data
331     char* pTagData = (char*)TagData.data();
332     const unsigned int tagDataLength = TagData.size();
333     unsigned int numBytesParsed = 0;
334
335     // if tag already exists, return false
336     // use EditTag explicitly instead
337     if ( FindTag(tag, pTagData, tagDataLength, numBytesParsed) )
338         return false;
339
340     // build new tag's base information
341     char newTagBase[Constants::BAM_TAG_ARRAYBASE_SIZE];
342     memcpy( newTagBase, tag.c_str(), Constants::BAM_TAG_TAGSIZE );
343     newTagBase[2] = Constants::BAM_TAG_TYPE_ARRAY;
344     newTagBase[3] = Constants::BAM_TAG_TYPE_UINT8;
345
346     // add number of array elements to newTagBase
347     const int32_t numElements  = values.size();
348     memcpy(newTagBase + 4, &numElements, sizeof(int32_t));
349
350     // copy current TagData string to temp buffer, leaving room for new tag's contents
351     const int newTagDataLength = tagDataLength +
352                                  Constants::BAM_TAG_ARRAYBASE_SIZE +
353                                  numElements*sizeof(uint8_t);
354     char* originalTagData = new char[newTagDataLength];
355     memcpy(originalTagData, TagData.c_str(), tagDataLength+1); // '+1' for TagData's null-term
356
357     // write newTagBase (removes old null term)
358     strcat(originalTagData + tagDataLength, (const char*)newTagBase);
359
360     // add vector elements to tag
361     int elementsBeginOffset = tagDataLength + Constants::BAM_TAG_ARRAYBASE_SIZE;
362     for ( int i = 0 ; i < numElements; ++i ) {
363         const uint8_t value = values.at(i);
364         memcpy(originalTagData + elementsBeginOffset + i*sizeof(uint8_t),
365                &value, sizeof(uint8_t));
366     }
367
368     // store temp buffer back in TagData
369     const char* newTagData = (const char*)originalTagData;
370     TagData.assign(newTagData, newTagDataLength);
371
372     delete[] originalTagData;
373
374     // return success
375     return true;
376 }
377
378 /*! \fn bool AddTag(const std::string& tag, const std::vector<int8_t>& values);
379     \brief Adds a numeric array field to the BAM tags.
380
381     Does NOT modify an existing tag - use \link BamAlignment::EditTag() \endlink instead.
382
383     \param tag    2-character tag name
384     \param values vector of int8_t values to store
385
386     \return \c true if the \b new tag was added successfully
387     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
388 */
389 bool BamAlignment::AddTag(const std::string& tag, const std::vector<int8_t>& values) {
390
391     // skip if core data not parsed
392     if ( SupportData.HasCoreOnly ) return false;
393
394     // check for valid tag length
395     if ( tag.size() != Constants::BAM_TAG_TAGSIZE ) return false;
396
397     // localize the tag data
398     char* pTagData = (char*)TagData.data();
399     const unsigned int tagDataLength = TagData.size();
400     unsigned int numBytesParsed = 0;
401
402     // if tag already exists, return false
403     // use EditTag explicitly instead
404     if ( FindTag(tag, pTagData, tagDataLength, numBytesParsed) )
405         return false;
406
407     // build new tag's base information
408     char newTagBase[Constants::BAM_TAG_ARRAYBASE_SIZE];
409     memcpy( newTagBase, tag.c_str(), Constants::BAM_TAG_TAGSIZE );
410     newTagBase[2] = Constants::BAM_TAG_TYPE_ARRAY;
411     newTagBase[3] = Constants::BAM_TAG_TYPE_INT8;
412
413     // add number of array elements to newTagBase
414     const int32_t numElements  = values.size();
415     memcpy(newTagBase + 4, &numElements, sizeof(int32_t));
416
417     // copy current TagData string to temp buffer, leaving room for new tag's contents
418     const int newTagDataLength = tagDataLength +
419                                  Constants::BAM_TAG_ARRAYBASE_SIZE +
420                                  numElements*sizeof(int8_t);
421     char* originalTagData = new char[newTagDataLength];
422     memcpy(originalTagData, TagData.c_str(), tagDataLength+1); // '+1' for TagData's null-term
423
424     // write newTagBase (removes old null term)
425     strcat(originalTagData + tagDataLength, (const char*)newTagBase);
426
427     // add vector elements to tag
428     int elementsBeginOffset = tagDataLength + Constants::BAM_TAG_ARRAYBASE_SIZE;
429     for ( int i = 0 ; i < numElements; ++i ) {
430         const int8_t value = values.at(i);
431         memcpy(originalTagData + elementsBeginOffset + i*sizeof(int8_t),
432                &value, sizeof(int8_t));
433     }
434
435     // store temp buffer back in TagData
436     const char* newTagData = (const char*)originalTagData;
437     TagData.assign(newTagData, newTagDataLength);
438
439     delete[] originalTagData;
440
441     // return success
442     return true;
443 }
444
445 /*! \fn bool AddTag(const std::string& tag, const std::vector<uint16_t>& values);
446     \brief Adds a numeric array field to the BAM tags.
447
448     Does NOT modify an existing tag - use \link BamAlignment::EditTag() \endlink instead.
449
450     \param tag    2-character tag name
451     \param values vector of uint16_t values to store
452
453     \return \c true if the \b new tag was added successfully
454     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
455 */
456 bool BamAlignment::AddTag(const std::string& tag, const std::vector<uint16_t>& values) {
457
458     // skip if core data not parsed
459     if ( SupportData.HasCoreOnly ) return false;
460
461     // check for valid tag length
462     if ( tag.size() != Constants::BAM_TAG_TAGSIZE ) return false;
463
464     // localize the tag data
465     char* pTagData = (char*)TagData.data();
466     const unsigned int tagDataLength = TagData.size();
467     unsigned int numBytesParsed = 0;
468
469     // if tag already exists, return false
470     // use EditTag explicitly instead
471     if ( FindTag(tag, pTagData, tagDataLength, numBytesParsed) )
472         return false;
473
474     // build new tag's base information
475     char newTagBase[Constants::BAM_TAG_ARRAYBASE_SIZE];
476     memcpy( newTagBase, tag.c_str(), Constants::BAM_TAG_TAGSIZE );
477     newTagBase[2] = Constants::BAM_TAG_TYPE_ARRAY;
478     newTagBase[3] = Constants::BAM_TAG_TYPE_UINT16;
479
480     // add number of array elements to newTagBase
481     const int32_t numElements  = values.size();
482     memcpy(newTagBase + 4, &numElements, sizeof(int32_t));
483
484     // copy current TagData string to temp buffer, leaving room for new tag's contents
485     const int newTagDataLength = tagDataLength +
486                                  Constants::BAM_TAG_ARRAYBASE_SIZE +
487                                  numElements*sizeof(uint16_t);
488     char* originalTagData = new char[newTagDataLength];
489     memcpy(originalTagData, TagData.c_str(), tagDataLength+1); // '+1' for TagData's null-term
490
491     // write newTagBase (removes old null term)
492     strcat(originalTagData + tagDataLength, (const char*)newTagBase);
493
494     // add vector elements to tag
495     int elementsBeginOffset = tagDataLength + Constants::BAM_TAG_ARRAYBASE_SIZE;
496     for ( int i = 0 ; i < numElements; ++i ) {
497         const uint16_t value = values.at(i);
498         memcpy(originalTagData + elementsBeginOffset + i*sizeof(uint16_t),
499                &value, sizeof(uint16_t));
500     }
501
502     // store temp buffer back in TagData
503     const char* newTagData = (const char*)originalTagData;
504     TagData.assign(newTagData, newTagDataLength);
505
506     delete[] originalTagData;
507
508     // return success
509     return true;
510 }
511
512 /*! \fn bool AddTag(const std::string& tag, const std::vector<int16_t>& values);
513     \brief Adds a numeric array field to the BAM tags.
514
515     Does NOT modify an existing tag - use \link BamAlignment::EditTag() \endlink instead.
516
517     \param tag    2-character tag name
518     \param values vector of int16_t values to store
519
520     \return \c true if the \b new tag was added successfully
521     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
522 */
523 bool BamAlignment::AddTag(const std::string& tag, const std::vector<int16_t>& values) {
524
525     // skip if core data not parsed
526     if ( SupportData.HasCoreOnly ) return false;
527
528     // check for valid tag length
529     if ( tag.size() != Constants::BAM_TAG_TAGSIZE ) return false;
530
531     // localize the tag data
532     char* pTagData = (char*)TagData.data();
533     const unsigned int tagDataLength = TagData.size();
534     unsigned int numBytesParsed = 0;
535
536     // if tag already exists, return false
537     // use EditTag explicitly instead
538     if ( FindTag(tag, pTagData, tagDataLength, numBytesParsed) )
539         return false;
540
541     // build new tag's base information
542     char newTagBase[Constants::BAM_TAG_ARRAYBASE_SIZE];
543     memcpy( newTagBase, tag.c_str(), Constants::BAM_TAG_TAGSIZE );
544     newTagBase[2] = Constants::BAM_TAG_TYPE_ARRAY;
545     newTagBase[3] = Constants::BAM_TAG_TYPE_INT16;
546
547     // add number of array elements to newTagBase
548     const int32_t numElements  = values.size();
549     memcpy(newTagBase + 4, &numElements, sizeof(int32_t));
550
551     // copy current TagData string to temp buffer, leaving room for new tag's contents
552     const int newTagDataLength = tagDataLength +
553                                  Constants::BAM_TAG_ARRAYBASE_SIZE +
554                                  numElements*sizeof(int16_t);
555     char* originalTagData = new char[newTagDataLength];
556     memcpy(originalTagData, TagData.c_str(), tagDataLength+1); // '+1' for TagData's null-term
557
558     // write newTagBase (removes old null term)
559     strcat(originalTagData + tagDataLength, (const char*)newTagBase);
560
561     // add vector elements to tag
562     int elementsBeginOffset = tagDataLength + Constants::BAM_TAG_ARRAYBASE_SIZE;
563     for ( int i = 0 ; i < numElements; ++i ) {
564         const int16_t value = values.at(i);
565         memcpy(originalTagData + elementsBeginOffset + i*sizeof(int16_t),
566                &value, sizeof(int16_t));
567     }
568
569     // store temp buffer back in TagData
570     const char* newTagData = (const char*)originalTagData;
571     TagData.assign(newTagData, newTagDataLength);
572
573     delete[] originalTagData;
574
575     // return success
576     return true;
577 }
578
579 /*! \fn bool AddTag(const std::string& tag, const std::vector<uint32_t>& values);
580     \brief Adds a numeric array field to the BAM tags.
581
582     Does NOT modify an existing tag - use \link BamAlignment::EditTag() \endlink instead.
583
584     \param tag    2-character tag name
585     \param values vector of uint32_t values to store
586
587     \return \c true if the \b new tag was added successfully
588     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
589 */
590 bool BamAlignment::AddTag(const std::string& tag, const std::vector<uint32_t>& values) {
591
592     // skip if core data not parsed
593     if ( SupportData.HasCoreOnly ) return false;
594
595     // check for valid tag length
596     if ( tag.size() != Constants::BAM_TAG_TAGSIZE ) return false;
597
598     // localize the tag data
599     char* pTagData = (char*)TagData.data();
600     const unsigned int tagDataLength = TagData.size();
601     unsigned int numBytesParsed = 0;
602
603     // if tag already exists, return false
604     // use EditTag explicitly instead
605     if ( FindTag(tag, pTagData, tagDataLength, numBytesParsed) )
606         return false;
607
608     // build new tag's base information
609     char newTagBase[Constants::BAM_TAG_ARRAYBASE_SIZE];
610     memcpy( newTagBase, tag.c_str(), Constants::BAM_TAG_TAGSIZE );
611     newTagBase[2] = Constants::BAM_TAG_TYPE_ARRAY;
612     newTagBase[3] = Constants::BAM_TAG_TYPE_UINT32;
613
614     // add number of array elements to newTagBase
615     const int32_t numElements  = values.size();
616     memcpy(newTagBase + 4, &numElements, sizeof(int32_t));
617
618     // copy current TagData string to temp buffer, leaving room for new tag's contents
619     const int newTagDataLength = tagDataLength +
620                                  Constants::BAM_TAG_ARRAYBASE_SIZE +
621                                  numElements*sizeof(uint32_t);
622     char* originalTagData = new char[newTagDataLength];
623     memcpy(originalTagData, TagData.c_str(), tagDataLength+1); // '+1' for TagData's null-term
624
625     // write newTagBase (removes old null term)
626     strcat(originalTagData + tagDataLength, (const char*)newTagBase);
627
628     // add vector elements to tag
629     int elementsBeginOffset = tagDataLength + Constants::BAM_TAG_ARRAYBASE_SIZE;
630     for ( int i = 0 ; i < numElements; ++i ) {
631         const uint32_t value = values.at(i);
632         memcpy(originalTagData + elementsBeginOffset + i*sizeof(uint32_t),
633                &value, sizeof(uint32_t));
634     }
635
636     // store temp buffer back in TagData
637     const char* newTagData = (const char*)originalTagData;
638     TagData.assign(newTagData, newTagDataLength);
639
640     delete[] originalTagData;
641
642     // return success
643     return true;
644 }
645
646 /*! \fn bool AddTag(const std::string& tag, const std::vector<int32_t>& values);
647     \brief Adds a numeric array field to the BAM tags.
648
649     Does NOT modify an existing tag - use \link BamAlignment::EditTag() \endlink instead.
650
651     \param tag    2-character tag name
652     \param values vector of int32_t values to store
653
654     \return \c true if the \b new tag was added successfully
655     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
656 */
657 bool BamAlignment::AddTag(const std::string& tag, const std::vector<int32_t>& values) {
658
659     // skip if core data not parsed
660     if ( SupportData.HasCoreOnly ) return false;
661
662     // check for valid tag length
663     if ( tag.size() != Constants::BAM_TAG_TAGSIZE ) return false;
664
665     // localize the tag data
666     char* pTagData = (char*)TagData.data();
667     const unsigned int tagDataLength = TagData.size();
668     unsigned int numBytesParsed = 0;
669
670     // if tag already exists, return false
671     // use EditTag explicitly instead
672     if ( FindTag(tag, pTagData, tagDataLength, numBytesParsed) )
673         return false;
674
675     // build new tag's base information
676     char newTagBase[Constants::BAM_TAG_ARRAYBASE_SIZE];
677     memcpy( newTagBase, tag.c_str(), Constants::BAM_TAG_TAGSIZE );
678     newTagBase[2] = Constants::BAM_TAG_TYPE_ARRAY;
679     newTagBase[3] = Constants::BAM_TAG_TYPE_INT32;
680
681     // add number of array elements to newTagBase
682     const int32_t numElements  = values.size();
683     memcpy(newTagBase + 4, &numElements, sizeof(int32_t));
684
685     // copy current TagData string to temp buffer, leaving room for new tag's contents
686     const int newTagDataLength = tagDataLength +
687                                  Constants::BAM_TAG_ARRAYBASE_SIZE +
688                                  numElements*sizeof(int32_t);
689     char* originalTagData = new char[newTagDataLength];
690     memcpy(originalTagData, TagData.c_str(), tagDataLength+1); // '+1' for TagData's null-term
691
692     // write newTagBase (removes old null term)
693     strcat(originalTagData + tagDataLength, (const char*)newTagBase);
694
695     // add vector elements to tag
696     int elementsBeginOffset = tagDataLength + Constants::BAM_TAG_ARRAYBASE_SIZE;
697     for ( int i = 0 ; i < numElements; ++i ) {
698         const int32_t value = values.at(i);
699         memcpy(originalTagData + elementsBeginOffset + i*sizeof(int32_t),
700                &value, sizeof(int32_t));
701     }
702
703     // store temp buffer back in TagData
704     const char* newTagData = (const char*)originalTagData;
705     TagData.assign(newTagData, newTagDataLength);
706
707     delete[] originalTagData;
708
709     // return success
710     return true;
711 }
712
713 /*! \fn bool AddTag(const std::string& tag, const std::vector<float>& values);
714     \brief Adds a numeric array field to the BAM tags.
715
716     Does NOT modify an existing tag - use \link BamAlignment::EditTag() \endlink instead.
717
718     \param tag    2-character tag name
719     \param values vector of float values to store
720
721     \return \c true if the \b new tag was added successfully
722     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
723 */
724 bool BamAlignment::AddTag(const std::string& tag, const std::vector<float>& values) {
725
726     // skip if core data not parsed
727     if ( SupportData.HasCoreOnly ) return false;
728
729     // check for valid tag length
730     if ( tag.size() != Constants::BAM_TAG_TAGSIZE ) return false;
731
732     // localize the tag data
733     char* pTagData = (char*)TagData.data();
734     const unsigned int tagDataLength = TagData.size();
735     unsigned int numBytesParsed = 0;
736
737     // if tag already exists, return false
738     // use EditTag explicitly instead
739     if ( FindTag(tag, pTagData, tagDataLength, numBytesParsed) )
740         return false;
741
742     // build new tag's base information
743     char newTagBase[Constants::BAM_TAG_ARRAYBASE_SIZE];
744     memcpy( newTagBase, tag.c_str(), Constants::BAM_TAG_TAGSIZE );
745     newTagBase[2] = Constants::BAM_TAG_TYPE_ARRAY;
746     newTagBase[3] = Constants::BAM_TAG_TYPE_FLOAT;
747
748     // add number of array elements to newTagBase
749     const int32_t numElements  = values.size();
750     memcpy(newTagBase + 4, &numElements, sizeof(int32_t));
751
752     // copy current TagData string to temp buffer, leaving room for new tag's contents
753     const int newTagDataLength = tagDataLength +
754                                  Constants::BAM_TAG_ARRAYBASE_SIZE +
755                                  numElements*sizeof(float);
756     char* originalTagData = new char[newTagDataLength];
757     memcpy(originalTagData, TagData.c_str(), tagDataLength+1); // '+1' for TagData's null-term
758
759     // write newTagBase (removes old null term)
760     strcat(originalTagData + tagDataLength, (const char*)newTagBase);
761
762     // add vector elements to tag
763     int elementsBeginOffset = tagDataLength + Constants::BAM_TAG_ARRAYBASE_SIZE;
764     for ( int i = 0 ; i < numElements; ++i ) {
765         const float value = values.at(i);
766         memcpy(originalTagData + elementsBeginOffset + i*sizeof(float),
767                &value, sizeof(float));
768     }
769
770     // store temp buffer back in TagData
771     const char* newTagData = (const char*)originalTagData;
772     TagData.assign(newTagData, newTagDataLength);
773
774     delete[] originalTagData;
775
776     // return success
777     return true;
778 }
779
780 /*! \fn bool BamAlignment::BuildCharData(void)
781     \brief Populates alignment string fields (read name, bases, qualities, tag data).
782
783     An alignment retrieved using BamReader::GetNextAlignmentCore() lacks this data.
784     Using that method makes parsing much quicker when only positional data is required.
785
786     However, if you later want to access the character data fields from such an alignment,
787     use this method to populate those fields. Provides ability to do 'lazy evaluation' of
788     alignment parsing.
789
790     \return \c true if character data populated successfully (or was already available to begin with)
791 */
792 bool BamAlignment::BuildCharData(void) {
793
794     // skip if char data already parsed
795     if ( !SupportData.HasCoreOnly )
796         return true;
797
798     // check system endianness
799     bool IsBigEndian = BamTools::SystemIsBigEndian();
800
801     // calculate character lengths/offsets
802     const unsigned int dataLength     = SupportData.BlockLength - Constants::BAM_CORE_SIZE;
803     const unsigned int seqDataOffset  = SupportData.QueryNameLength + (SupportData.NumCigarOperations * 4);
804     const unsigned int qualDataOffset = seqDataOffset + (SupportData.QuerySequenceLength+1)/2;
805     const unsigned int tagDataOffset  = qualDataOffset + SupportData.QuerySequenceLength;
806     const unsigned int tagDataLength  = dataLength - tagDataOffset;
807
808     // check offsets to see what char data exists
809     const bool hasSeqData  = ( seqDataOffset  < dataLength );
810     const bool hasQualData = ( qualDataOffset < dataLength );
811     const bool hasTagData  = ( tagDataOffset  < dataLength );
812
813     // set up char buffers
814     const char* allCharData = SupportData.AllCharData.data();
815     const char* seqData     = ( hasSeqData  ? (((const char*)allCharData) + seqDataOffset)  : (const char*)0 );
816     const char* qualData    = ( hasQualData ? (((const char*)allCharData) + qualDataOffset) : (const char*)0 );
817           char* tagData     = ( hasTagData  ? (((char*)allCharData) + tagDataOffset)        : (char*)0 );
818
819     // store alignment name (relies on null char in name as terminator)
820     Name.assign((const char*)(allCharData));
821
822     // save query sequence
823     QueryBases.clear();
824     if ( hasSeqData ) {
825         QueryBases.reserve(SupportData.QuerySequenceLength);
826         for (unsigned int i = 0; i < SupportData.QuerySequenceLength; ++i) {
827             char singleBase = Constants::BAM_DNA_LOOKUP[ ( (seqData[(i/2)] >> (4*(1-(i%2)))) & 0xf ) ];
828             QueryBases.append(1, singleBase);
829         }
830     }
831
832     // save qualities, converting from numeric QV to 'FASTQ-style' ASCII character
833     Qualities.clear();
834     if ( hasQualData ) {
835         Qualities.reserve(SupportData.QuerySequenceLength);
836         for (unsigned int i = 0; i < SupportData.QuerySequenceLength; ++i) {
837             char singleQuality = (char)(qualData[i]+33);
838             Qualities.append(1, singleQuality);
839         }
840     }
841
842     // clear previous AlignedBases
843     AlignedBases.clear();
844
845     // if QueryBases has data, build AlignedBases using CIGAR data
846     // otherwise, AlignedBases will remain empty (this case IS allowed)
847     if ( !QueryBases.empty() ) {
848
849         // resize AlignedBases
850         AlignedBases.reserve(SupportData.QuerySequenceLength);
851
852         // iterate over CigarOps
853         int k = 0;
854         vector<CigarOp>::const_iterator cigarIter = CigarData.begin();
855         vector<CigarOp>::const_iterator cigarEnd  = CigarData.end();
856         for ( ; cigarIter != cigarEnd; ++cigarIter ) {
857             const CigarOp& op = (*cigarIter);
858
859             switch (op.Type) {
860
861                 // for 'M', 'I', '=', 'X' - write bases
862                 case (Constants::BAM_CIGAR_MATCH_CHAR)    :
863                 case (Constants::BAM_CIGAR_INS_CHAR)      :
864                 case (Constants::BAM_CIGAR_SEQMATCH_CHAR) :
865                 case (Constants::BAM_CIGAR_MISMATCH_CHAR) :
866                     AlignedBases.append(QueryBases.substr(k, op.Length));
867                     // fall through
868
869                 // for 'S' - soft clip, do not write bases
870                 // but increment placeholder 'k'
871                 case (Constants::BAM_CIGAR_SOFTCLIP_CHAR) :
872                     k += op.Length;
873                     break;
874
875                 // for 'D' - write gap character
876                 case (Constants::BAM_CIGAR_DEL_CHAR) :
877                     AlignedBases.append(op.Length, Constants::BAM_DNA_DEL);
878                     break;
879
880                 // for 'P' - write padding character
881                 case (Constants::BAM_CIGAR_PAD_CHAR) :
882                     AlignedBases.append( op.Length, Constants::BAM_DNA_PAD );
883                     break;
884
885                 // for 'N' - write N's, skip bases in original query sequence
886                 case (Constants::BAM_CIGAR_REFSKIP_CHAR) :
887                     AlignedBases.append( op.Length, Constants::BAM_DNA_N );
888                     break;
889
890                 // for 'H' - hard clip, do nothing to AlignedBases, move to next op
891                 case (Constants::BAM_CIGAR_HARDCLIP_CHAR) :
892                     break;
893
894                 // shouldn't get here
895                 default:
896                     cerr << "BamAlignment ERROR: invalid CIGAR operation type: "
897                          << op.Type << endl;
898                     exit(1);
899             }
900         }
901     }
902
903     // save tag data
904     TagData.clear();
905     if ( hasTagData ) {
906         if ( IsBigEndian ) {
907             int i = 0;
908             while ( (unsigned int)i < tagDataLength ) {
909
910                 i += Constants::BAM_TAG_TAGSIZE;  // skip tag chars (e.g. "RG", "NM", etc.)
911                 const char type = tagData[i];     // get tag type at position i
912                 ++i;                              // move i past tag type
913
914                 switch (type) {
915
916                     case(Constants::BAM_TAG_TYPE_ASCII) :
917                     case(Constants::BAM_TAG_TYPE_INT8)  :
918                     case(Constants::BAM_TAG_TYPE_UINT8) :
919                         // no endian swapping necessary for single-byte data
920                         ++i;
921                         break;
922
923                     case(Constants::BAM_TAG_TYPE_INT16)  :
924                     case(Constants::BAM_TAG_TYPE_UINT16) :
925                         BamTools::SwapEndian_16p(&tagData[i]);
926                         i += sizeof(uint16_t);
927                         break;
928
929                     case(Constants::BAM_TAG_TYPE_FLOAT)  :
930                     case(Constants::BAM_TAG_TYPE_INT32)  :
931                     case(Constants::BAM_TAG_TYPE_UINT32) :
932                         BamTools::SwapEndian_32p(&tagData[i]);
933                         i += sizeof(uint32_t);
934                         break;
935
936                     case(Constants::BAM_TAG_TYPE_HEX) :
937                     case(Constants::BAM_TAG_TYPE_STRING) :
938                         // no endian swapping necessary for hex-string/string data
939                         while ( tagData[i] )
940                             ++i;
941                         // increment one more for null terminator
942                         ++i;
943                         break;
944
945                     case(Constants::BAM_TAG_TYPE_ARRAY) :
946
947                     {
948                         // read array type
949                         const char arrayType = tagData[i];
950                         ++i;
951
952                         // swap endian-ness of number of elements in place, then retrieve for loop
953                         BamTools::SwapEndian_32p(&tagData[i]);
954                         int32_t numElements;
955                         memcpy(&numElements, &tagData[i], sizeof(uint32_t));
956                         i += sizeof(uint32_t);
957
958                         // swap endian-ness of array elements
959                         for ( int j = 0; j < numElements; ++j ) {
960                             switch (arrayType) {
961                                 case (Constants::BAM_TAG_TYPE_INT8)  :
962                                 case (Constants::BAM_TAG_TYPE_UINT8) :
963                                     // no endian-swapping necessary
964                                     ++i;
965                                     break;
966                                 case (Constants::BAM_TAG_TYPE_INT16)  :
967                                 case (Constants::BAM_TAG_TYPE_UINT16) :
968                                     BamTools::SwapEndian_16p(&tagData[i]);
969                                     i += sizeof(uint16_t);
970                                     break;
971                                 case (Constants::BAM_TAG_TYPE_FLOAT)  :
972                                 case (Constants::BAM_TAG_TYPE_INT32)  :
973                                 case (Constants::BAM_TAG_TYPE_UINT32) :
974                                     BamTools::SwapEndian_32p(&tagData[i]);
975                                     i += sizeof(uint32_t);
976                                     break;
977                                 default:
978                                     // error case
979                                     cerr << "BamAlignment ERROR: unknown binary array type encountered: "
980                                          << arrayType << endl;
981                                     return false;
982                             }
983                         }
984
985                         break;
986                     }
987
988                     // shouldn't get here
989                     default :
990                         cerr << "BamAlignment ERROR: invalid tag value type: "
991                              << type << endl;
992                         exit(1);
993                 }
994             }
995         }
996
997         // store tagData in alignment
998         TagData.resize(tagDataLength);
999         memcpy((char*)TagData.data(), tagData, tagDataLength);
1000     }
1001
1002     // clear the core-only flag
1003     SupportData.HasCoreOnly = false;
1004
1005     // return success
1006     return true;
1007 }
1008
1009 /*! \fn bool BamAlignment::EditTag(const std::string& tag, const std::string& type, const std::string& value)
1010     \brief Edits a BAM tag field containing string data.
1011
1012     If \a tag does not exist, a new entry is created.
1013
1014     \param tag   2-character tag name
1015     \param type  1-character tag type (must be "Z" or "H")
1016     \param value string data to store
1017
1018     \return \c true if the tag was modified/created successfully
1019
1020     \sa BamAlignment::RemoveTag()
1021     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
1022 */
1023 bool BamAlignment::EditTag(const std::string& tag, const std::string& type, const std::string& value) {
1024   
1025     // skip if core data not parsed
1026     if ( SupportData.HasCoreOnly ) return false;
1027
1028     // validate tag/type size & that type is OK for string value
1029     if ( !IsValidSize(tag, type) ) return false;
1030     if ( type.at(0) != Constants::BAM_TAG_TYPE_STRING &&
1031          type.at(0) != Constants::BAM_TAG_TYPE_HEX )
1032         return false;
1033   
1034     // localize the tag data
1035     char* pOriginalTagData = (char*)TagData.data();
1036     char* pTagData = pOriginalTagData;
1037     const unsigned int originalTagDataLength = TagData.size();
1038     
1039     unsigned int newTagDataLength = 0;
1040     unsigned int numBytesParsed = 0;
1041     
1042     // if tag found
1043     if ( FindTag(tag, pTagData, originalTagDataLength, numBytesParsed) ) {
1044         
1045         // make sure array is more than big enough
1046         char* newTagData = new char[originalTagDataLength + value.size()];  
1047
1048         // copy original tag data up til desired tag
1049         const unsigned int beginningTagDataLength = numBytesParsed;
1050         newTagDataLength += beginningTagDataLength;
1051         memcpy(newTagData, pOriginalTagData, numBytesParsed);
1052       
1053         // copy new @value in place of current tag data
1054         const unsigned int dataLength = strlen(value.c_str());
1055         memcpy(newTagData + beginningTagDataLength, (char*)value.c_str(), dataLength+1 );
1056         
1057         // skip to next tag (if tag for removal is last, return true) 
1058         const char* pTagStorageType = pTagData - 1;
1059         if ( !SkipToNextTag(*pTagStorageType, pTagData, numBytesParsed) )
1060             return true;
1061          
1062         // copy everything from current tag (the next one after tag for removal) to end
1063         const unsigned int skippedDataLength = (numBytesParsed - beginningTagDataLength);
1064         const unsigned int endTagOffset      = beginningTagDataLength + dataLength + 1;
1065         const unsigned int endTagDataLength  = originalTagDataLength - beginningTagDataLength - skippedDataLength;
1066         memcpy(newTagData + endTagOffset, pTagData, endTagDataLength);
1067         
1068         // ensure null-terminator
1069         newTagData[ endTagOffset + endTagDataLength + 1 ] = 0;
1070         
1071         // save new tag data
1072         TagData.assign(newTagData, endTagOffset + endTagDataLength);
1073
1074         delete[] newTagData;
1075
1076         return true;
1077     }
1078     
1079     // tag not found, attempt AddTag
1080     else return AddTag(tag, type, value);
1081 }
1082
1083 /*! \fn bool BamAlignment::EditTag(const std::string& tag, const std::string& type, const uint32_t& value)
1084     \brief Edits a BAM tag field containing unsigned integer data.
1085
1086     If \a tag does not exist, a new entry is created.
1087
1088     \param tag   2-character tag name
1089     \param type  1-character tag type (must NOT be "f", "Z", "H", or "B")
1090     \param value unsigned integer data to store
1091
1092     \return \c true if the tag was modified/created successfully
1093
1094     \sa BamAlignment::RemoveTag()
1095     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
1096 */
1097 bool BamAlignment::EditTag(const std::string& tag, const std::string& type, const uint32_t& value) {
1098   
1099     // skip if core data not parsed
1100     if ( SupportData.HasCoreOnly ) return false;
1101
1102     // validate tag/type size & that type is OK for uint32_t value
1103     if ( !IsValidSize(tag, type) ) return false;
1104     if ( type.at(0) == Constants::BAM_TAG_TYPE_FLOAT  ||
1105          type.at(0) == Constants::BAM_TAG_TYPE_STRING ||
1106          type.at(0) == Constants::BAM_TAG_TYPE_HEX    ||
1107          type.at(0) == Constants::BAM_TAG_TYPE_ARRAY
1108        )
1109     {
1110         return false;
1111     }
1112
1113     // localize the tag data
1114     char* pOriginalTagData = (char*)TagData.data();
1115     char* pTagData = pOriginalTagData;
1116     const unsigned int originalTagDataLength = TagData.size();
1117     
1118     unsigned int newTagDataLength = 0;
1119     unsigned int numBytesParsed = 0;
1120     
1121     // if tag found
1122     if ( FindTag(tag, pTagData, originalTagDataLength, numBytesParsed) ) {
1123         
1124         // make sure array is more than big enough
1125         char* newTagData = new char[originalTagDataLength + sizeof(value)];  
1126
1127         // copy original tag data up til desired tag
1128         const unsigned int beginningTagDataLength = numBytesParsed;
1129         newTagDataLength += beginningTagDataLength;
1130         memcpy(newTagData, pOriginalTagData, numBytesParsed);
1131       
1132         // copy new @value in place of current tag data
1133         union { uint32_t value; char valueBuffer[sizeof(uint32_t)]; } un;
1134         un.value = value;
1135         memcpy(newTagData + beginningTagDataLength, un.valueBuffer, sizeof(uint32_t));
1136         
1137         // skip to next tag (if tag for removal is last, return true) 
1138         const char* pTagStorageType = pTagData - 1;
1139         if ( !SkipToNextTag(*pTagStorageType, pTagData, numBytesParsed) )
1140             return true;
1141          
1142         // copy everything from current tag (the next one after tag for removal) to end
1143         const unsigned int skippedDataLength = (numBytesParsed - beginningTagDataLength);
1144         const unsigned int endTagOffset      = beginningTagDataLength + sizeof(uint32_t);
1145         const unsigned int endTagDataLength  = originalTagDataLength - beginningTagDataLength - skippedDataLength;
1146         memcpy(newTagData + endTagOffset, pTagData, endTagDataLength);
1147         
1148         // ensure null-terminator
1149         newTagData[ endTagOffset + endTagDataLength + 1 ] = 0;
1150         
1151         // save new tag data
1152         TagData.assign(newTagData, endTagOffset + endTagDataLength);
1153
1154         delete[] newTagData;
1155
1156         return true;
1157     }
1158     
1159     // tag not found, attempt AddTag
1160     else return AddTag(tag, type, value);
1161 }
1162
1163 /*! \fn bool BamAlignment::EditTag(const std::string& tag, const std::string& type, const int32_t& value)
1164     \brief Edits a BAM tag field containing signed integer data.
1165
1166     If \a tag does not exist, a new entry is created.
1167
1168     \param tag   2-character tag name
1169     \param type  1-character tag type (must NOT be "f", "Z", "H", or "B")
1170     \param value signed integer data to store
1171
1172     \return \c true if the tag was modified/created successfully
1173
1174     \sa BamAlignment::RemoveTag()
1175     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
1176 */
1177 bool BamAlignment::EditTag(const std::string& tag, const std::string& type, const int32_t& value) {
1178     return EditTag(tag, type, (const uint32_t&)value);
1179 }
1180
1181 /*! \fn bool BamAlignment::EditTag(const std::string& tag, const std::string& type, const float& value)
1182     \brief Edits a BAM tag field containing floating-point data.
1183
1184     If \a tag does not exist, a new entry is created.
1185
1186     \param tag   2-character tag name
1187     \param type  1-character tag type (must NOT be "Z", "H", or "B")
1188     \param value float data to store
1189
1190     \return \c true if the tag was modified/created successfully
1191
1192     \sa BamAlignment::RemoveTag()
1193     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
1194 */
1195 bool BamAlignment::EditTag(const std::string& tag, const std::string& type, const float& value) {
1196   
1197     // skip if core data not parsed
1198     if ( SupportData.HasCoreOnly ) return false;
1199
1200     // validate tag/type size & that type is OK for float value
1201     if ( !IsValidSize(tag, type) ) return false;
1202     if ( type.at(0) == Constants::BAM_TAG_TYPE_STRING ||
1203          type.at(0) == Constants::BAM_TAG_TYPE_HEX    ||
1204          type.at(0) == Constants::BAM_TAG_TYPE_ARRAY
1205        )
1206     {
1207         return false;
1208     }
1209
1210      // localize the tag data
1211     char* pOriginalTagData = (char*)TagData.data();
1212     char* pTagData = pOriginalTagData;
1213     const unsigned int originalTagDataLength = TagData.size();
1214     
1215     unsigned int newTagDataLength = 0;
1216     unsigned int numBytesParsed = 0;
1217     
1218     // if tag found
1219     if ( FindTag(tag, pTagData, originalTagDataLength, numBytesParsed) ) {
1220         
1221         // make sure array is more than big enough
1222         char* newTagData = new char[originalTagDataLength + sizeof(value)];  
1223
1224         // copy original tag data up til desired tag
1225         const unsigned int beginningTagDataLength = numBytesParsed;
1226         newTagDataLength += beginningTagDataLength;
1227         memcpy(newTagData, pOriginalTagData, numBytesParsed);
1228       
1229         // copy new @value in place of current tag data
1230         union { float value; char valueBuffer[sizeof(float)]; } un;
1231         un.value = value;
1232         memcpy(newTagData + beginningTagDataLength, un.valueBuffer, sizeof(float));
1233         
1234         // skip to next tag (if tag for removal is last, return true) 
1235         const char* pTagStorageType = pTagData - 1;
1236         if ( !SkipToNextTag(*pTagStorageType, pTagData, numBytesParsed) )
1237             return true;
1238          
1239         // copy everything from current tag (the next one after tag for removal) to end
1240         const unsigned int skippedDataLength = (numBytesParsed - beginningTagDataLength);
1241         const unsigned int endTagOffset      = beginningTagDataLength + sizeof(float);
1242         const unsigned int endTagDataLength  = originalTagDataLength - beginningTagDataLength - skippedDataLength;
1243         memcpy(newTagData + endTagOffset, pTagData, endTagDataLength);
1244         
1245         // ensure null-terminator
1246         newTagData[ endTagOffset + endTagDataLength + 1 ] = 0;
1247         
1248         // save new tag data
1249         TagData.assign(newTagData, endTagOffset + endTagDataLength);
1250
1251         delete[] newTagData;
1252
1253         return true;
1254     }
1255     
1256     // tag not found, attempt AddTag
1257     else return AddTag(tag, type, value);
1258 }
1259
1260 /*! \fn bool EditTag(const std::string& tag, const std::vector<uint8_t>& values);
1261     \brief Edits a BAM tag field containing a numeric array.
1262
1263     If \a tag does not exist, a new entry is created.
1264
1265     \param tag   2-character tag name
1266     \param value vector of uint8_t values to store
1267
1268     \return \c true if the tag was modified/created successfully
1269     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
1270 */
1271 bool BamAlignment::EditTag(const std::string& tag, const std::vector<uint8_t>& values) {
1272
1273     // can't do anything if TagData not parsed
1274     if ( SupportData.HasCoreOnly )
1275         return false;
1276
1277     // remove existing tag if present
1278     if ( HasTag(tag) )
1279         RemoveTag(tag);
1280
1281     // add tag record with new values
1282     return AddTag(tag, values);
1283 }
1284
1285 /*! \fn bool EditTag(const std::string& tag, const std::vector<int8_t>& values);
1286     \brief Edits a BAM tag field containing a numeric array.
1287
1288     If \a tag does not exist, a new entry is created.
1289
1290     \param tag   2-character tag name
1291     \param value vector of int8_t values to store
1292
1293     \return \c true if the tag was modified/created successfully
1294     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
1295 */
1296 bool BamAlignment::EditTag(const std::string& tag, const std::vector<int8_t>& values) {
1297
1298     // can't do anything if TagData not parsed
1299     if ( SupportData.HasCoreOnly )
1300         return false;
1301
1302     // remove existing tag if present
1303     if ( HasTag(tag) )
1304         RemoveTag(tag);
1305
1306     // add tag record with new values
1307     return AddTag(tag, values);
1308 }
1309
1310 /*! \fn bool EditTag(const std::string& tag, const std::vector<uint16_t>& values);
1311     \brief Edits a BAM tag field containing a numeric array.
1312
1313     If \a tag does not exist, a new entry is created.
1314
1315     \param tag   2-character tag name
1316     \param value vector of uint16_t values to store
1317
1318     \return \c true if the tag was modified/created successfully
1319     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
1320 */
1321 bool BamAlignment::EditTag(const std::string& tag, const std::vector<uint16_t>& values) {
1322
1323     // can't do anything if TagData not parsed
1324     if ( SupportData.HasCoreOnly )
1325         return false;
1326
1327     // remove existing tag if present
1328     if ( HasTag(tag) )
1329         RemoveTag(tag);
1330
1331     // add tag record with new values
1332     return AddTag(tag, values);
1333 }
1334
1335 /*! \fn bool EditTag(const std::string& tag, const std::vector<int16_t>& values);
1336     \brief Edits a BAM tag field containing a numeric array.
1337
1338     If \a tag does not exist, a new entry is created.
1339
1340     \param tag   2-character tag name
1341     \param value vector of int16_t values to store
1342
1343     \return \c true if the tag was modified/created successfully
1344     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
1345 */
1346 bool BamAlignment::EditTag(const std::string& tag, const std::vector<int16_t>& values) {
1347
1348     // can't do anything if TagData not parsed
1349     if ( SupportData.HasCoreOnly )
1350         return false;
1351
1352     // remove existing tag if present
1353     if ( HasTag(tag) )
1354         RemoveTag(tag);
1355
1356     // add tag record with new values
1357     return AddTag(tag, values);
1358 }
1359
1360 /*! \fn bool EditTag(const std::string& tag, const std::vector<uint32_t>& values);
1361     \brief Edits a BAM tag field containing a numeric array.
1362
1363     If \a tag does not exist, a new entry is created.
1364
1365     \param tag   2-character tag name
1366     \param value vector of uint32_t values to store
1367
1368     \return \c true if the tag was modified/created successfully
1369     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
1370 */
1371 bool BamAlignment::EditTag(const std::string& tag, const std::vector<uint32_t>& values) {
1372
1373     // can't do anything if TagData not parsed
1374     if ( SupportData.HasCoreOnly )
1375         return false;
1376
1377     // remove existing tag if present
1378     if ( HasTag(tag) )
1379         RemoveTag(tag);
1380
1381     // add tag record with new values
1382     return AddTag(tag, values);
1383 }
1384
1385 /*! \fn bool EditTag(const std::string& tag, const std::vector<int32_t>& values);
1386     \brief Edits a BAM tag field containing a numeric array.
1387
1388     If \a tag does not exist, a new entry is created.
1389
1390     \param tag   2-character tag name
1391     \param value vector of int32_t values to store
1392
1393     \return \c true if the tag was modified/created successfully
1394     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
1395 */
1396 bool BamAlignment::EditTag(const std::string& tag, const std::vector<int32_t>& values) {
1397
1398     // can't do anything if TagData not parsed
1399     if ( SupportData.HasCoreOnly )
1400         return false;
1401
1402     // remove existing tag if present
1403     if ( HasTag(tag) )
1404         RemoveTag(tag);
1405
1406     // add tag record with new values
1407     return AddTag(tag, values);
1408 }
1409
1410 /*! \fn bool EditTag(const std::string& tag, const std::vector<float>& values);
1411     \brief Edits a BAM tag field containing a numeric array.
1412
1413     If \a tag does not exist, a new entry is created.
1414
1415     \param tag   2-character tag name
1416     \param value vector of float values to store
1417
1418     \return \c true if the tag was modified/created successfully
1419     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
1420 */
1421 bool BamAlignment::EditTag(const std::string& tag, const std::vector<float>& values) {
1422
1423     // can't do anything if TagData not parsed
1424     if ( SupportData.HasCoreOnly )
1425         return false;
1426
1427     // remove existing tag if present
1428     if ( HasTag(tag) )
1429         RemoveTag(tag);
1430
1431     // add tag record with new values
1432     return AddTag(tag, values);
1433 }
1434
1435 /*! \fn bool BamAlignment::FindTag(const std::string& tag, char*& pTagData, const unsigned int& tagDataLength, unsigned int& numBytesParsed)
1436     \internal
1437
1438     Searches for requested tag in BAM tag data.
1439
1440     \param tag            requested 2-character tag name
1441     \param pTagData       pointer to current position in BamAlignment::TagData
1442     \param tagDataLength  length of BamAlignment::TagData
1443     \param numBytesParsed number of bytes parsed so far
1444
1445     \return \c true if found
1446
1447     \post If \a tag is found, \a pTagData will point to the byte where the tag data begins.
1448           \a numBytesParsed will correspond to the position in the full TagData string.
1449
1450 */
1451 bool BamAlignment::FindTag(const std::string& tag,
1452                            char*& pTagData,
1453                            const unsigned int& tagDataLength,
1454                            unsigned int& numBytesParsed) const
1455 {
1456
1457     while ( numBytesParsed < tagDataLength ) {
1458
1459         const char* pTagType        = pTagData;
1460         const char* pTagStorageType = pTagData + 2;
1461         pTagData       += 3;
1462         numBytesParsed += 3;
1463
1464         // check the current tag, return true on match
1465         if ( strncmp(pTagType, tag.c_str(), 2) == 0 )
1466             return true;
1467
1468         // get the storage class and find the next tag
1469         if ( *pTagStorageType == '\0' ) return false;
1470         if ( !SkipToNextTag(*pTagStorageType, pTagData, numBytesParsed) ) return false;
1471         if ( *pTagData == '\0' ) return false;
1472     }
1473
1474     // checked all tags, none match
1475     return false;
1476 }
1477
1478 /*! \fn bool BamAlignment::GetEditDistance(uint32_t& editDistance) const
1479     \brief Retrieves value of edit distance tag ("NM").
1480
1481     \deprecated Instead use BamAlignment::GetTag()
1482         \code
1483             BamAlignment::GetTag("NM", editDistance);
1484         \endcode
1485
1486     \param editDistance destination for retrieved value
1487
1488     \return \c true if found
1489 */
1490 bool BamAlignment::GetEditDistance(uint32_t& editDistance) const {
1491     return GetTag("NM", (uint32_t&)editDistance);
1492 }
1493
1494 /*! \fn int BamAlignment::GetEndPosition(bool usePadded = false, bool zeroBased = true) const
1495     \brief Calculates alignment end position, based on starting position and CIGAR data.
1496
1497     \param usePadded Inserted bases affect reported position. Default is false, so that reported
1498                      position stays 'sync-ed' with reference coordinates.
1499     \param zeroBased Return (BAM standard) 0-based coordinate. Setting this to false can be useful
1500                      when using BAM data with half-open formats (e.g. BED).
1501
1502     \return alignment end position
1503 */
1504 int BamAlignment::GetEndPosition(bool usePadded, bool zeroBased) const {
1505
1506     // initialize alignment end to starting position
1507     int alignEnd = Position;
1508
1509     // iterate over cigar operations
1510     vector<CigarOp>::const_iterator cigarIter = CigarData.begin();
1511     vector<CigarOp>::const_iterator cigarEnd  = CigarData.end();
1512     for ( ; cigarIter != cigarEnd; ++cigarIter) {
1513         const char cigarType = (*cigarIter).Type;
1514         const uint32_t& cigarLength = (*cigarIter).Length;
1515
1516         if ( cigarType == Constants::BAM_CIGAR_MATCH_CHAR ||
1517              cigarType == Constants::BAM_CIGAR_DEL_CHAR ||
1518              cigarType == Constants::BAM_CIGAR_REFSKIP_CHAR )
1519             alignEnd += cigarLength;
1520         else if ( usePadded && cigarType == Constants::BAM_CIGAR_INS_CHAR )
1521             alignEnd += cigarLength;
1522     }
1523
1524     // adjust for zero-based coordinates, if requested
1525     if ( zeroBased ) alignEnd -= 1;
1526
1527     // return result
1528     return alignEnd;
1529 }
1530
1531 /*! \fn bool BamAlignment::GetReadGroup(std::string& readGroup) const
1532     \brief Retrieves value of read group tag ("RG").
1533
1534     \deprecated Instead use BamAlignment::GetTag()
1535         \code
1536             BamAlignment::GetTag("RG", readGroup);
1537         \endcode
1538
1539     \param readGroup destination for retrieved value
1540
1541     \return \c true if found
1542 */
1543 bool BamAlignment::GetReadGroup(std::string& readGroup) const {
1544     return GetTag("RG", readGroup);
1545 }
1546
1547 /*! \fn bool BamAlignment::GetTag(const std::string& tag, std::string& destination) const
1548     \brief Retrieves the string value associated with a BAM tag.
1549
1550     \param tag         2-character tag name
1551     \param destination destination for retrieved value
1552
1553     \return \c true if found
1554 */
1555 bool BamAlignment::GetTag(const std::string& tag, std::string& destination) const {
1556
1557     // make sure tag data exists
1558     if ( SupportData.HasCoreOnly || TagData.empty() ) 
1559         return false;
1560
1561     // localize the tag data
1562     char* pTagData = (char*)TagData.data();
1563     const unsigned int tagDataLength = TagData.size();
1564     unsigned int numBytesParsed = 0;
1565     
1566     // if tag found
1567     if ( FindTag(tag, pTagData, tagDataLength, numBytesParsed) ) {
1568         const unsigned int dataLength = strlen(pTagData);
1569         destination.clear();
1570         destination.resize(dataLength);
1571         memcpy( (char*)destination.data(), pTagData, dataLength );
1572         return true;
1573     }
1574     
1575     // tag not found, return failure
1576     return false;
1577 }
1578
1579 /*! \fn bool BamAlignment::GetTag(const std::string& tag, uint32_t& destination) const
1580     \brief Retrieves the unsigned integer value associated with a BAM tag.
1581
1582     \param tag         2-character tag name
1583     \param destination destination for retrieved value
1584
1585     \return \c true if found
1586 */
1587 bool BamAlignment::GetTag(const std::string& tag, uint32_t& destination) const {
1588   
1589     // make sure tag data exists
1590     if ( SupportData.HasCoreOnly || TagData.empty() ) 
1591         return false;
1592
1593     // localize the tag data
1594     char* pTagData = (char*)TagData.data();
1595     const unsigned int tagDataLength = TagData.size();
1596     unsigned int numBytesParsed = 0;
1597     
1598     // if tag found
1599     if ( FindTag(tag, pTagData, tagDataLength, numBytesParsed) ) {
1600         
1601         // determine data byte-length
1602         const char type = *(pTagData - 1);
1603         int destinationLength = 0;
1604         switch (type) {
1605
1606             // 1 byte data
1607             case (Constants::BAM_TAG_TYPE_ASCII) :
1608             case (Constants::BAM_TAG_TYPE_INT8)  :
1609             case (Constants::BAM_TAG_TYPE_UINT8) :
1610                 destinationLength = 1;
1611                 break;
1612
1613             // 2 byte data
1614             case (Constants::BAM_TAG_TYPE_INT16)  :
1615             case (Constants::BAM_TAG_TYPE_UINT16) :
1616                 destinationLength = 2;
1617                 break;
1618
1619             // 4 byte data
1620             case (Constants::BAM_TAG_TYPE_INT32)  :
1621             case (Constants::BAM_TAG_TYPE_UINT32) :
1622                 destinationLength = 4;
1623                 break;
1624
1625             // unsupported type for integer destination (float or var-length strings)
1626             case (Constants::BAM_TAG_TYPE_FLOAT)  :
1627             case (Constants::BAM_TAG_TYPE_STRING) :
1628             case (Constants::BAM_TAG_TYPE_HEX)    :
1629             case (Constants::BAM_TAG_TYPE_ARRAY)  :
1630                 cerr << "BamAlignment ERROR: cannot store tag of type " << type
1631                      << " in integer destination" << endl;
1632                 return false;
1633
1634             // unknown tag type
1635             default:
1636                 cerr << "BamAlignment ERROR: unknown tag type encountered: "
1637                      << type << endl;
1638                 return false;
1639         }
1640           
1641         // store in destination
1642         destination = 0;
1643         memcpy(&destination, pTagData, destinationLength);
1644         return true;
1645     }
1646     
1647     // tag not found, return failure
1648     return false;
1649 }
1650
1651 /*! \fn bool BamAlignment::GetTag(const std::string& tag, int32_t& destination) const
1652     \brief Retrieves the signed integer value associated with a BAM tag.
1653
1654     \param tag         2-character tag name
1655     \param destination destination for retrieved value
1656
1657     \return \c true if found
1658 */
1659 bool BamAlignment::GetTag(const std::string& tag, int32_t& destination) const {
1660     return GetTag(tag, (uint32_t&)destination);
1661 }
1662
1663 /*! \fn bool BamAlignment::GetTag(const std::string& tag, float& destination) const
1664     \brief Retrieves the floating-point value associated with a BAM tag.
1665
1666     \param tag         2-character tag name
1667     \param destination destination for retrieved value
1668
1669     \return \c true if found
1670 */
1671 bool BamAlignment::GetTag(const std::string& tag, float& destination) const {
1672   
1673     // make sure tag data exists
1674     if ( SupportData.HasCoreOnly || TagData.empty() ) 
1675         return false;
1676
1677     // localize the tag data
1678     char* pTagData = (char*)TagData.data();
1679     const unsigned int tagDataLength = TagData.size();
1680     unsigned int numBytesParsed = 0;
1681     
1682     // if tag found
1683     if ( FindTag(tag, pTagData, tagDataLength, numBytesParsed) ) {
1684         
1685         // determine data byte-length
1686         const char type = *(pTagData - 1);
1687         int destinationLength = 0;
1688         switch (type) {
1689
1690             // 1 byte data
1691             case (Constants::BAM_TAG_TYPE_ASCII) :
1692             case (Constants::BAM_TAG_TYPE_INT8)  :
1693             case (Constants::BAM_TAG_TYPE_UINT8) :
1694                 destinationLength = 1;
1695                 break;
1696
1697             // 2 byte data
1698             case (Constants::BAM_TAG_TYPE_INT16)  :
1699             case (Constants::BAM_TAG_TYPE_UINT16) :
1700                 destinationLength = 2;
1701                 break;
1702
1703             // 4 byte data
1704             case (Constants::BAM_TAG_TYPE_FLOAT)  :
1705             case (Constants::BAM_TAG_TYPE_INT32)  :
1706             case (Constants::BAM_TAG_TYPE_UINT32) :
1707                 destinationLength = 4;
1708                 break;
1709             
1710             // unsupported type (var-length strings)
1711             case (Constants::BAM_TAG_TYPE_STRING) :
1712             case (Constants::BAM_TAG_TYPE_HEX)    :
1713             case (Constants::BAM_TAG_TYPE_ARRAY)  :
1714                 cerr << "BamAlignment ERROR: cannot store tag of type " << type
1715                      << " in float destination" << endl;
1716                 return false;
1717
1718             // unknown tag type
1719             default:
1720                 cerr << "BamAlignment ERROR: unknown tag type encountered: "
1721                      << type << endl;
1722                 return false;
1723         }
1724           
1725         // store in destination
1726         destination = 0.0;
1727         memcpy(&destination, pTagData, destinationLength);
1728         return true;
1729     }
1730     
1731     // tag not found, return failure
1732     return false;
1733 }
1734
1735 /*! \fn bool BamAlignment::GetTag(const std::string& tag, std::vector<uint32_t>& destination) const
1736     \brief Retrieves the numeric array data associated with a BAM tag
1737
1738     \param tag         2-character tag name
1739     \param destination destination for retrieved data
1740
1741     \return \c true if found
1742 */
1743 bool BamAlignment::GetTag(const std::string& tag, std::vector<uint32_t>& destination) const {
1744
1745     // make sure tag data exists
1746     if ( SupportData.HasCoreOnly || TagData.empty() )
1747         return false;
1748
1749     // localize the tag data
1750     char* pTagData = (char*)TagData.data();
1751     const unsigned int tagDataLength = TagData.size();
1752     unsigned int numBytesParsed = 0;
1753
1754     // return false if tag not found
1755     if ( !FindTag(tag, pTagData, tagDataLength, numBytesParsed) )
1756         return false;
1757
1758     // check that tag is array type
1759     const char tagType = *(pTagData - 1);
1760     if ( tagType != Constants::BAM_TAG_TYPE_ARRAY ) {
1761         cerr << "BamAlignment ERROR: Cannot store non-array data from tag: "
1762              << tag << " in array destination" << endl;
1763         return false;
1764     }
1765
1766     // calculate length of each element in tag's array
1767     const char elementType = *pTagData;
1768     ++pTagData;
1769     int elementLength = 0;
1770     switch ( elementType ) {
1771         case (Constants::BAM_TAG_TYPE_ASCII) :
1772         case (Constants::BAM_TAG_TYPE_INT8)  :
1773         case (Constants::BAM_TAG_TYPE_UINT8) :
1774             elementLength = sizeof(uint8_t);
1775             break;
1776
1777         case (Constants::BAM_TAG_TYPE_INT16)  :
1778         case (Constants::BAM_TAG_TYPE_UINT16) :
1779             elementLength = sizeof(uint16_t);
1780             break;
1781
1782         case (Constants::BAM_TAG_TYPE_INT32)  :
1783         case (Constants::BAM_TAG_TYPE_UINT32) :
1784             elementLength = sizeof(uint32_t);
1785             break;
1786
1787         // unsupported type for integer destination (float or var-length data)
1788         case (Constants::BAM_TAG_TYPE_FLOAT)  :
1789         case (Constants::BAM_TAG_TYPE_STRING) :
1790         case (Constants::BAM_TAG_TYPE_HEX)    :
1791         case (Constants::BAM_TAG_TYPE_ARRAY)  :
1792             cerr << "BamAlignment ERROR: array element type: " << elementType
1793                  << " cannot be stored in integer value" << endl;
1794             return false;
1795
1796         // unknown tag type
1797         default:
1798             cerr << "BamAlignment ERROR: unknown element type encountered: "
1799                  << elementType << endl;
1800             return false;
1801     }
1802
1803     // get number of elements
1804     int32_t numElements;
1805     memcpy(&numElements, pTagData, sizeof(int32_t));
1806     pTagData += 4;
1807     destination.clear();
1808     destination.reserve(numElements);
1809
1810     // read in elements
1811     uint32_t value;
1812     for ( int i = 0 ; i < numElements; ++i ) {
1813         memcpy(&value, pTagData, sizeof(uint32_t));
1814         pTagData += sizeof(uint32_t);
1815         destination.push_back(value);
1816     }
1817
1818     // return success
1819     return false;
1820 }
1821
1822 /*! \fn bool BamAlignment::GetTag(const std::string& tag, std::vector<int32_t>& destination) const
1823     \brief Retrieves the numeric array data associated with a BAM tag
1824
1825     \param tag         2-character tag name
1826     \param destination destination for retrieved data
1827
1828     \return \c true if found
1829 */
1830 bool BamAlignment::GetTag(const std::string& tag, std::vector<int32_t>& destination) const {
1831
1832     // make sure tag data exists
1833     if ( SupportData.HasCoreOnly || TagData.empty() )
1834         return false;
1835
1836     // localize the tag data
1837     char* pTagData = (char*)TagData.data();
1838     const unsigned int tagDataLength = TagData.size();
1839     unsigned int numBytesParsed = 0;
1840
1841     // return false if tag not found
1842     if ( !FindTag(tag, pTagData, tagDataLength, numBytesParsed) )
1843         return false;
1844
1845     // check that tag is array type
1846     const char tagType = *(pTagData - 1);
1847     if ( tagType != Constants::BAM_TAG_TYPE_ARRAY ) {
1848         cerr << "BamAlignment ERROR: Cannot store non-array data from tag: "
1849              << tag << " in array destination" << endl;
1850         return false;
1851     }
1852
1853     // calculate length of each element in tag's array
1854     const char elementType = *pTagData;
1855     ++pTagData;
1856     int elementLength = 0;
1857     switch ( elementType ) {
1858         case (Constants::BAM_TAG_TYPE_ASCII) :
1859         case (Constants::BAM_TAG_TYPE_INT8)  :
1860         case (Constants::BAM_TAG_TYPE_UINT8) :
1861             elementLength = sizeof(uint8_t);
1862             break;
1863
1864         case (Constants::BAM_TAG_TYPE_INT16)  :
1865         case (Constants::BAM_TAG_TYPE_UINT16) :
1866             elementLength = sizeof(uint16_t);
1867             break;
1868
1869         case (Constants::BAM_TAG_TYPE_INT32)  :
1870         case (Constants::BAM_TAG_TYPE_UINT32) :
1871             elementLength = sizeof(uint32_t);
1872             break;
1873
1874         // unsupported type for integer destination (float or var-length data)
1875         case (Constants::BAM_TAG_TYPE_FLOAT)  :
1876         case (Constants::BAM_TAG_TYPE_STRING) :
1877         case (Constants::BAM_TAG_TYPE_HEX)    :
1878         case (Constants::BAM_TAG_TYPE_ARRAY)  :
1879             cerr << "BamAlignment ERROR: array element type: " << elementType
1880                  << " cannot be stored in integer value" << endl;
1881             return false;
1882
1883         // unknown tag type
1884         default:
1885             cerr << "BamAlignment ERROR: unknown element type encountered: "
1886                  << elementType << endl;
1887             return false;
1888     }
1889
1890     // get number of elements
1891     int32_t numElements;
1892     memcpy(&numElements, pTagData, sizeof(int32_t));
1893     pTagData += 4;
1894     destination.clear();
1895     destination.reserve(numElements);
1896
1897     // read in elements
1898     int32_t value;
1899     for ( int i = 0 ; i < numElements; ++i ) {
1900         memcpy(&value, pTagData, sizeof(int32_t));
1901         pTagData += sizeof(int32_t);
1902         destination.push_back(value);
1903     }
1904
1905     // return success
1906     return false;
1907
1908 }
1909
1910 /*! \fn bool BamAlignment::GetTag(const std::string& tag, std::vector<float>& destination) const
1911     \brief Retrieves the numeric array data associated with a BAM tag
1912
1913     \param tag         2-character tag name
1914     \param destination destination for retrieved data
1915
1916     \return \c true if found
1917 */
1918 bool BamAlignment::GetTag(const std::string& tag, std::vector<float>& destination) const {
1919
1920     // make sure tag data exists
1921     if ( SupportData.HasCoreOnly || TagData.empty() )
1922         return false;
1923
1924     // localize the tag data
1925     char* pTagData = (char*)TagData.data();
1926     const unsigned int tagDataLength = TagData.size();
1927     unsigned int numBytesParsed = 0;
1928
1929     // return false if tag not found
1930     if ( !FindTag(tag, pTagData, tagDataLength, numBytesParsed) )
1931         return false;
1932
1933     // check that tag is array type
1934     const char tagType = *(pTagData - 1);
1935     if ( tagType != Constants::BAM_TAG_TYPE_ARRAY ) {
1936         cerr << "BamAlignment ERROR: Cannot store non-array data from tag: "
1937              << tag << " in array destination" << endl;
1938         return false;
1939     }
1940
1941     // calculate length of each element in tag's array
1942     const char elementType = *pTagData;
1943     ++pTagData;
1944     int elementLength = 0;
1945     switch ( elementType ) {
1946         case (Constants::BAM_TAG_TYPE_ASCII) :
1947         case (Constants::BAM_TAG_TYPE_INT8)  :
1948         case (Constants::BAM_TAG_TYPE_UINT8) :
1949             elementLength = sizeof(uint8_t);
1950             break;
1951
1952         case (Constants::BAM_TAG_TYPE_INT16)  :
1953         case (Constants::BAM_TAG_TYPE_UINT16) :
1954             elementLength = sizeof(uint16_t);
1955             break;
1956
1957         case (Constants::BAM_TAG_TYPE_INT32)  :
1958         case (Constants::BAM_TAG_TYPE_UINT32) :
1959         case (Constants::BAM_TAG_TYPE_FLOAT)  :
1960             elementLength = sizeof(uint32_t);
1961             break;
1962
1963         // unsupported type for float destination (var-length data)
1964         case (Constants::BAM_TAG_TYPE_STRING) :
1965         case (Constants::BAM_TAG_TYPE_HEX)    :
1966         case (Constants::BAM_TAG_TYPE_ARRAY)  :
1967             cerr << "BamAlignment ERROR: array element type: " << elementType
1968                  << " cannot be stored in float value" << endl;
1969             return false;
1970
1971         // unknown tag type
1972         default:
1973             cerr << "BamAlignment ERROR: unknown element type encountered: "
1974                  << elementType << endl;
1975             return false;
1976     }
1977
1978     // get number of elements
1979     int32_t numElements;
1980     memcpy(&numElements, pTagData, sizeof(int32_t));
1981     pTagData += 4;
1982     destination.clear();
1983     destination.reserve(numElements);
1984
1985     // read in elements
1986     float value;
1987     for ( int i = 0 ; i < numElements; ++i ) {
1988         memcpy(&value, pTagData, sizeof(float));
1989         pTagData += sizeof(float);
1990         destination.push_back(value);
1991     }
1992
1993     // return success
1994     return false;
1995 }
1996
1997 /*! \fn bool BamAlignment::GetTagType(const std::string& tag, char& type) const
1998     \brief Retrieves the BAM tag type-code associated with requested tag name.
1999
2000     \param tag  2-character tag name
2001     \param type destination for the retrieved (1-character) tag type
2002
2003     \return \c true if found
2004     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
2005 */
2006 bool BamAlignment::GetTagType(const std::string& tag, char& type) const {
2007   
2008     // make sure tag data exists
2009     if ( SupportData.HasCoreOnly || TagData.empty() ) 
2010         return false;
2011
2012     // localize the tag data
2013     char* pTagData = (char*)TagData.data();
2014     const unsigned int tagDataLength = TagData.size();
2015     unsigned int numBytesParsed = 0;
2016     
2017     // lookup tag
2018     if ( FindTag(tag, pTagData, tagDataLength, numBytesParsed) ) {
2019         
2020         // retrieve tag type code
2021         type = *(pTagData - 1);
2022         
2023         // validate that type is a proper BAM tag type
2024         switch (type) {
2025             case (Constants::BAM_TAG_TYPE_ASCII)  :
2026             case (Constants::BAM_TAG_TYPE_INT8)   :
2027             case (Constants::BAM_TAG_TYPE_UINT8)  :
2028             case (Constants::BAM_TAG_TYPE_INT16)  :
2029             case (Constants::BAM_TAG_TYPE_UINT16) :
2030             case (Constants::BAM_TAG_TYPE_INT32)  :
2031             case (Constants::BAM_TAG_TYPE_UINT32) :
2032             case (Constants::BAM_TAG_TYPE_FLOAT)  :
2033             case (Constants::BAM_TAG_TYPE_STRING) :
2034             case (Constants::BAM_TAG_TYPE_HEX)    :
2035             case (Constants::BAM_TAG_TYPE_ARRAY)  :
2036                 return true;
2037
2038             // unknown tag type
2039             default:
2040                 cerr << "BamAlignment ERROR: unknown tag type encountered: "
2041                      << type << endl;
2042                 return false;
2043         }
2044     }
2045     
2046     // tag not found, return failure
2047     return false;
2048 }
2049
2050 /*! \fn bool BamAlignment::HasTag(const std::string& tag) const
2051     \brief Returns true if alignment has a record for requested tag.
2052     \param tag 2-character tag name
2053     \return \c true if alignment has a record for tag
2054 */
2055 bool BamAlignment::HasTag(const std::string& tag) const {
2056
2057     // return false if no tag data present
2058     if ( SupportData.HasCoreOnly || TagData.empty() )
2059         return false;
2060
2061     // localize the tag data for lookup
2062     char* pTagData = (char*)TagData.data();
2063     const unsigned int tagDataLength = TagData.size();
2064     unsigned int numBytesParsed = 0;
2065
2066     // if result of tag lookup
2067     return FindTag(tag, pTagData, tagDataLength, numBytesParsed);
2068 }
2069
2070 /*! \fn bool BamAlignment::IsDuplicate(void) const
2071     \return \c true if this read is a PCR duplicate
2072 */
2073 bool BamAlignment::IsDuplicate(void) const {
2074     return ( (AlignmentFlag & Constants::BAM_ALIGNMENT_DUPLICATE) != 0 );
2075 }
2076
2077 /*! \fn bool BamAlignment::IsFailedQC(void) const
2078     \return \c true if this read failed quality control
2079 */
2080 bool BamAlignment::IsFailedQC(void) const {
2081     return ( (AlignmentFlag & Constants::BAM_ALIGNMENT_QC_FAILED) != 0 );
2082 }
2083
2084 /*! \fn bool BamAlignment::IsFirstMate(void) const
2085     \return \c true if alignment is first mate on paired-end read
2086 */
2087 bool BamAlignment::IsFirstMate(void) const {
2088     return ( (AlignmentFlag & Constants::BAM_ALIGNMENT_READ_1) != 0 );
2089 }
2090
2091 /*! \fn bool BamAlignment::IsMapped(void) const
2092     \return \c true if alignment is mapped
2093 */
2094 bool BamAlignment::IsMapped(void) const {
2095     return ( (AlignmentFlag & Constants::BAM_ALIGNMENT_UNMAPPED) == 0 );
2096 }
2097
2098 /*! \fn bool BamAlignment::IsMateMapped(void) const
2099     \return \c true if alignment's mate is mapped
2100 */
2101 bool BamAlignment::IsMateMapped(void) const {
2102     return ( (AlignmentFlag & Constants::BAM_ALIGNMENT_MATE_UNMAPPED) == 0 );
2103 }
2104
2105 /*! \fn bool BamAlignment::IsMateReverseStrand(void) const
2106     \return \c true if alignment's mate mapped to reverse strand
2107 */
2108 bool BamAlignment::IsMateReverseStrand(void) const {
2109     return ( (AlignmentFlag & Constants::BAM_ALIGNMENT_MATE_REVERSE_STRAND) != 0 );
2110 }
2111
2112 /*! \fn bool BamAlignment::IsPaired(void) const
2113     \return \c true if alignment part of paired-end read
2114 */
2115 bool BamAlignment::IsPaired(void) const {
2116     return ( (AlignmentFlag & Constants::BAM_ALIGNMENT_PAIRED) != 0 );
2117 }
2118
2119 /*! \fn bool BamAlignment::IsPrimaryAlignment(void) const
2120     \return \c true if reported position is primary alignment
2121 */
2122 bool BamAlignment::IsPrimaryAlignment(void) const  {
2123     return ( (AlignmentFlag & Constants::BAM_ALIGNMENT_SECONDARY) == 0 );
2124 }
2125
2126 /*! \fn bool BamAlignment::IsProperPair(void) const
2127     \return \c true if alignment is part of read that satisfied paired-end resolution
2128 */
2129 bool BamAlignment::IsProperPair(void) const {
2130     return ( (AlignmentFlag & Constants::BAM_ALIGNMENT_PROPER_PAIR) != 0 );
2131 }
2132
2133 /*! \fn bool BamAlignment::IsReverseStrand(void) const
2134     \return \c true if alignment mapped to reverse strand
2135 */
2136 bool BamAlignment::IsReverseStrand(void) const {
2137     return ( (AlignmentFlag & Constants::BAM_ALIGNMENT_REVERSE_STRAND) != 0 );
2138 }
2139
2140 /*! \fn bool BamAlignment::IsSecondMate(void) const
2141     \return \c true if alignment is second mate on read
2142 */
2143 bool BamAlignment::IsSecondMate(void) const {
2144     return ( (AlignmentFlag & Constants::BAM_ALIGNMENT_READ_2) != 0 );
2145 }
2146
2147 /*! \fn bool BamAlignment::IsValidSize(const string& tag, const string& type) const
2148     \internal
2149
2150     Checks that tag name & type strings are expected sizes.
2151     \a tag  should have length
2152     \a type should have length 1
2153
2154     \param tag  BAM tag name
2155     \param type BAM tag type-code
2156
2157     \return \c true if both \a tag and \a type are correct sizes
2158 */
2159 bool BamAlignment::IsValidSize(const string& tag, const string& type) const {
2160     return (tag.size()  == Constants::BAM_TAG_TAGSIZE) &&
2161            (type.size() == Constants::BAM_TAG_TYPESIZE);
2162 }
2163
2164 /*! \fn bool BamAlignment::RemoveTag(const std::string& tag)
2165     \brief Removes field from BAM tags.
2166
2167     \return \c true if tag was removed successfully (or didn't exist before)
2168 */
2169 bool BamAlignment::RemoveTag(const std::string& tag) {
2170   
2171     // skip if no tag data available
2172     if ( SupportData.HasCoreOnly || TagData.empty() )
2173         return false;
2174   
2175     // localize the tag data
2176     char* pOriginalTagData = (char*)TagData.data();
2177     char* pTagData = pOriginalTagData;
2178     const unsigned int originalTagDataLength = TagData.size();
2179     unsigned int newTagDataLength = 0;
2180     unsigned int numBytesParsed = 0;
2181     
2182     // if tag found
2183     if ( FindTag(tag, pTagData, originalTagDataLength, numBytesParsed) ) {
2184         
2185         char* newTagData = new char[originalTagDataLength];
2186
2187         // copy original tag data up til desired tag
2188         pTagData       -= 3;
2189         numBytesParsed -= 3;
2190         const unsigned int beginningTagDataLength = numBytesParsed;
2191         newTagDataLength += beginningTagDataLength;
2192         memcpy(newTagData, pOriginalTagData, numBytesParsed);
2193         
2194         // skip to next tag (if tag for removal is last, return true) 
2195         const char* pTagStorageType = pTagData + 2;
2196         pTagData       += 3;
2197         numBytesParsed += 3;
2198         if ( !SkipToNextTag(*pTagStorageType, pTagData, numBytesParsed) )
2199             return true;
2200          
2201         // copy everything from current tag (the next one after tag for removal) to end
2202         const unsigned int skippedDataLength = (numBytesParsed - beginningTagDataLength);
2203         const unsigned int endTagDataLength = originalTagDataLength - beginningTagDataLength - skippedDataLength;
2204         memcpy(newTagData + beginningTagDataLength, pTagData, endTagDataLength );
2205         
2206         // save new tag data
2207         TagData.assign(newTagData, beginningTagDataLength + endTagDataLength);
2208
2209         delete[] newTagData;
2210
2211         return true;
2212     }
2213     
2214     // tag not found, no removal - return failure
2215     return false;
2216 }
2217
2218 /*! \fn void BamAlignment::SetIsDuplicate(bool ok)
2219     \brief Sets value of "PCR duplicate" flag to \a ok.
2220 */
2221 void BamAlignment::SetIsDuplicate(bool ok) {
2222     if (ok) AlignmentFlag |=  Constants::BAM_ALIGNMENT_DUPLICATE;
2223     else    AlignmentFlag &= ~Constants::BAM_ALIGNMENT_DUPLICATE;
2224 }
2225
2226 /*! \fn void BamAlignment::SetIsFailedQC(bool ok)
2227     \brief Sets "failed quality control" flag to \a ok.
2228 */
2229 void BamAlignment::SetIsFailedQC(bool ok) {
2230     if (ok) AlignmentFlag |=  Constants::BAM_ALIGNMENT_QC_FAILED;
2231     else    AlignmentFlag &= ~Constants::BAM_ALIGNMENT_QC_FAILED;
2232 }
2233
2234 /*! \fn void BamAlignment::SetIsFirstMate(bool ok)
2235     \brief Sets "alignment is first mate" flag to \a ok.
2236 */
2237 void BamAlignment::SetIsFirstMate(bool ok) {
2238     if (ok) AlignmentFlag |=  Constants::BAM_ALIGNMENT_READ_1;
2239     else    AlignmentFlag &= ~Constants::BAM_ALIGNMENT_READ_1;
2240 }
2241
2242 /*! \fn void BamAlignment::SetIsMapped(bool ok)
2243     \brief Sets "alignment is mapped" flag to \a ok.
2244 */
2245 void BamAlignment::SetIsMapped(bool ok) {
2246     if (ok) AlignmentFlag &= ~Constants::BAM_ALIGNMENT_UNMAPPED;
2247     else    AlignmentFlag |=  Constants::BAM_ALIGNMENT_UNMAPPED;
2248 }
2249
2250 /*! \fn void BamAlignment::SetIsMateMapped(bool ok)
2251     \brief Sets "alignment's mate is mapped" flag to \a ok.
2252 */
2253 void BamAlignment::SetIsMateMapped(bool ok) {
2254     if (ok) AlignmentFlag &= ~Constants::BAM_ALIGNMENT_MATE_UNMAPPED;
2255     else    AlignmentFlag |=  Constants::BAM_ALIGNMENT_MATE_UNMAPPED;
2256 }
2257
2258 /*! \fn void BamAlignment::SetIsMateUnmapped(bool ok)
2259     \brief Complement of using SetIsMateMapped().
2260     \deprecated For sake of symmetry with the query methods
2261     \sa IsMateMapped(), SetIsMateMapped()
2262 */
2263 void BamAlignment::SetIsMateUnmapped(bool ok) {
2264     SetIsMateMapped(!ok);
2265 }
2266
2267 /*! \fn void BamAlignment::SetIsMateReverseStrand(bool ok)
2268     \brief Sets "alignment's mate mapped to reverse strand" flag to \a ok.
2269 */
2270 void BamAlignment::SetIsMateReverseStrand(bool ok) {
2271     if (ok) AlignmentFlag |=  Constants::BAM_ALIGNMENT_MATE_REVERSE_STRAND;
2272     else    AlignmentFlag &= ~Constants::BAM_ALIGNMENT_MATE_REVERSE_STRAND;
2273 }
2274
2275 /*! \fn void BamAlignment::SetIsPaired(bool ok)
2276     \brief Sets "alignment part of paired-end read" flag to \a ok.
2277 */
2278 void BamAlignment::SetIsPaired(bool ok) {
2279     if (ok) AlignmentFlag |=  Constants::BAM_ALIGNMENT_PAIRED;
2280     else    AlignmentFlag &= ~Constants::BAM_ALIGNMENT_PAIRED;
2281 }
2282
2283 /*! \fn void BamAlignment::SetIsPrimaryAlignment(bool ok)
2284     \brief Sets "position is primary alignment" flag to \a ok.
2285 */
2286 void BamAlignment::SetIsPrimaryAlignment(bool ok) {
2287     if (ok) AlignmentFlag &= ~Constants::BAM_ALIGNMENT_SECONDARY;
2288     else    AlignmentFlag |=  Constants::BAM_ALIGNMENT_SECONDARY;
2289 }
2290
2291 /*! \fn void BamAlignment::SetIsProperPair(bool ok)
2292     \brief Sets "alignment is part of read that satisfied paired-end resolution" flag to \a ok.
2293 */
2294 void BamAlignment::SetIsProperPair(bool ok) {
2295     if (ok) AlignmentFlag |=  Constants::BAM_ALIGNMENT_PROPER_PAIR;
2296     else    AlignmentFlag &= ~Constants::BAM_ALIGNMENT_PROPER_PAIR;
2297 }
2298
2299 /*! \fn void BamAlignment::SetIsReverseStrand(bool ok)
2300     \brief Sets "alignment mapped to reverse strand" flag to \a ok.
2301 */
2302 void BamAlignment::SetIsReverseStrand(bool ok) {
2303     if (ok) AlignmentFlag |=  Constants::BAM_ALIGNMENT_REVERSE_STRAND;
2304     else    AlignmentFlag &= ~Constants::BAM_ALIGNMENT_REVERSE_STRAND;
2305 }
2306
2307 /*! \fn void BamAlignment::SetIsSecondaryAlignment(bool ok)
2308     \brief Complement of using SetIsPrimaryAlignment().
2309     \deprecated For sake of symmetry with the query methods
2310     \sa IsPrimaryAlignment(), SetIsPrimaryAlignment()
2311 */
2312 void BamAlignment::SetIsSecondaryAlignment(bool ok) {
2313     SetIsPrimaryAlignment(!ok);
2314 }
2315
2316 /*! \fn void BamAlignment::SetIsSecondMate(bool ok)
2317     \brief Sets "alignment is second mate on read" flag to \a ok.
2318 */
2319 void BamAlignment::SetIsSecondMate(bool ok) {
2320     if (ok) AlignmentFlag |=  Constants::BAM_ALIGNMENT_READ_2;
2321     else    AlignmentFlag &= ~Constants::BAM_ALIGNMENT_READ_2;
2322 }
2323
2324 /*! \fn void BamAlignment::SetIsUnmapped(bool ok)
2325     \brief Complement of using SetIsMapped().
2326     \deprecated For sake of symmetry with the query methods
2327     \sa IsMapped(), SetIsMapped()
2328 */
2329 void BamAlignment::SetIsUnmapped(bool ok) {
2330     SetIsMapped(!ok);
2331 }
2332
2333 /*! \fn bool BamAlignment::SkipToNextTag(const char storageType, char*& pTagData, unsigned int& numBytesParsed)
2334     \internal
2335
2336     Moves to next available tag in tag data string
2337
2338     \param storageType    BAM tag type-code that determines how far to move cursor
2339     \param pTagData       pointer to current position (cursor) in tag string
2340     \param numBytesParsed report of how many bytes were parsed (cumulatively)
2341
2342     \return \c if storageType was a recognized BAM tag type
2343     \post \a pTagData will point to the byte where the next tag data begins.
2344           \a numBytesParsed will correspond to the cursor's position in the full TagData string.
2345 */
2346 bool BamAlignment::SkipToNextTag(const char storageType,
2347                                  char*& pTagData,
2348                                  unsigned int& numBytesParsed) const
2349 {
2350     switch (storageType) {
2351
2352         case (Constants::BAM_TAG_TYPE_ASCII) :
2353         case (Constants::BAM_TAG_TYPE_INT8)  :
2354         case (Constants::BAM_TAG_TYPE_UINT8) :
2355             ++numBytesParsed;
2356             ++pTagData;
2357             break;
2358
2359         case (Constants::BAM_TAG_TYPE_INT16)  :
2360         case (Constants::BAM_TAG_TYPE_UINT16) :
2361             numBytesParsed += sizeof(uint16_t);
2362             pTagData       += sizeof(uint16_t);
2363             break;
2364
2365         case (Constants::BAM_TAG_TYPE_FLOAT)  :
2366         case (Constants::BAM_TAG_TYPE_INT32)  :
2367         case (Constants::BAM_TAG_TYPE_UINT32) :
2368             numBytesParsed += sizeof(uint32_t);
2369             pTagData       += sizeof(uint32_t);
2370             break;
2371
2372         case (Constants::BAM_TAG_TYPE_STRING) :
2373         case (Constants::BAM_TAG_TYPE_HEX)    :
2374             while( *pTagData ) {
2375                 ++numBytesParsed;
2376                 ++pTagData;
2377             }
2378             // increment for null-terminator
2379             ++numBytesParsed;
2380             ++pTagData;
2381             break;
2382
2383         case (Constants::BAM_TAG_TYPE_ARRAY) :
2384
2385         {
2386             // read array type
2387             const char arrayType = *pTagData;
2388             ++numBytesParsed;
2389             ++pTagData;
2390
2391             // read number of elements
2392             int32_t numElements;
2393             memcpy(&numElements, pTagData, sizeof(uint32_t)); // already endian-swapped if necessary
2394             numBytesParsed += sizeof(uint32_t);
2395             pTagData       += sizeof(uint32_t);
2396
2397             // calculate number of bytes to skip
2398             int bytesToSkip = 0;
2399             switch (arrayType) {
2400                 case (Constants::BAM_TAG_TYPE_INT8)  :
2401                 case (Constants::BAM_TAG_TYPE_UINT8) :
2402                     bytesToSkip = numElements;
2403                     break;
2404                 case (Constants::BAM_TAG_TYPE_INT16)  :
2405                 case (Constants::BAM_TAG_TYPE_UINT16) :
2406                     bytesToSkip = numElements*sizeof(uint16_t);
2407                     break;
2408                 case (Constants::BAM_TAG_TYPE_FLOAT)  :
2409                 case (Constants::BAM_TAG_TYPE_INT32)  :
2410                 case (Constants::BAM_TAG_TYPE_UINT32) :
2411                     bytesToSkip = numElements*sizeof(uint32_t);
2412                     break;
2413                 default:
2414                     cerr << "BamAlignment ERROR: unknown binary array type encountered: "
2415                          << arrayType << endl;
2416                     return false;
2417             }
2418
2419             // skip binary array contents
2420             numBytesParsed += bytesToSkip;
2421             pTagData       += bytesToSkip;
2422             break;
2423         }
2424
2425         default:
2426             cerr << "BamAlignment ERROR: unknown tag type encountered"
2427                  << storageType << endl;
2428             return false;
2429     }
2430
2431     // return success
2432     return true;
2433 }