func BaseAtPos(record *sam.Record, pos int) (byte, bool) {
	index, overlap := indexAtPos(record, pos)
	if !overlap {
		return 0, false
	} else if index >= 0 {
		return record.Seq.BaseChar(index), true
	}
	return 0, true
}
BaseAtPos returns the base at reference pos (0 based) from record,
if the mapped part of the read overlaps pos.  If not, return (0,
false).  If pos is in the mapped portion of the read, but the
reference base was skipped, then the returned value will be (0,
true).
package bam
import (
	"fmt"
	"github.com/grailbio/hts/sam"
)
func IsPaired(record *sam.Record) bool {
	return record.Flags&sam.Paired != 0
}
func IsProperPair(record *sam.Record) bool {
	return record.Flags&sam.ProperPair != 0
}
func IsUnmapped(record *sam.Record) bool {
	return record.Flags&sam.Unmapped != 0
}
func IsMateUnmapped(record *sam.Record) bool {
	return record.Flags&sam.MateUnmapped != 0
}
func IsReverse(record *sam.Record) bool {
	return record.Flags&sam.Reverse != 0
}
func IsMateReverse(record *sam.Record) bool {
	return record.Flags&sam.MateReverse != 0
}
func IsRead1(record *sam.Record) bool {
	return record.Flags&sam.Read1 != 0
}
func IsRead2(record *sam.Record) bool {
	return record.Flags&sam.Read2 != 0
}
func IsSecondary(record *sam.Record) bool {
	return record.Flags&sam.Secondary != 0
}
func IsQCFail(record *sam.Record) bool {
	return record.Flags&sam.QCFail != 0
}
func IsDuplicate(record *sam.Record) bool {
	return record.Flags&sam.Duplicate != 0
}
func IsLinearDuplicate(record *sam.Record) bool {
	dupType, err := record.LinearDup()
	if err != nil {
		panic(err)
	}
	return dupType == sam.LinearDuplicate
}
func IsSupplementary(record *sam.Record) bool {
	return record.Flags&sam.Supplementary != 0
}
func IsPrimary(record *sam.Record) bool {
	return record.Flags&sam.Secondary == 0 && record.Flags&sam.Supplementary == 0
}
func HasNoMappedMate(record *sam.Record) bool {
	return (record.Flags&sam.Paired) == 0 || (record.Flags&sam.MateUnmapped) != 0
}
func ClearAuxTags(r *sam.Record, tagsToRemove []sam.Tag) {
	found := 0
	for i := range r.AuxFields {
		tag := r.AuxFields[i].Tag()
		for _, toRemove := range tagsToRemove {
			if tag == toRemove {
				found++
			}
		}
	}
	if found > 0 {
		newAux := make([]sam.Aux, len(r.AuxFields)-found)
		size := 0
		for i := range r.AuxFields {
			tag := r.AuxFields[i].Tag()
			keepAux := true
			for _, toRemove := range tagsToRemove {
				if tag == toRemove {
					keepAux = false
					break
				}
			}
			if keepAux {
				newAux[size] = r.AuxFields[i]
				size++
			}
		}
		r.AuxFields = newAux
	}
}
type StrandType int
const (
	StrandNone StrandType = iota
	StrandFwd
	StrandRev
)
func GetStrand(flags sam.Flags) StrandType {
	maskedFlag := flags & (sam.Unmapped | sam.Reverse | sam.Read1 | sam.Read2)
	if (maskedFlag == sam.Read1) || (maskedFlag == (sam.Reverse | sam.Read2)) {
		return StrandFwd
	}
	if (maskedFlag == sam.Read2) || (maskedFlag == (sam.Reverse | sam.Read1)) {
		return StrandRev
	}
	return StrandNone
}
func IsReversedRead(record *sam.Record) bool {
	return (record.Flags & sam.Reverse) != 0
}
func IsQCFailed(record *sam.Record) bool {
	return (record.Flags & sam.QCFail) != 0
}
func LeftClipDistance(record *sam.Record) int {
	total := 0
	for _, op := range record.Cigar {
		if op.Type() == sam.CigarSoftClipped || op.Type() == sam.CigarHardClipped {
			total += op.Len()
		} else {
			break
		}
	}
	return total
}
func RightClipDistance(record *sam.Record) int {
	total := 0
	for i := len(record.Cigar) - 1; i >= 0; i-- {
		op := record.Cigar[i]
		if op.Type() == sam.CigarSoftClipped || op.Type() == sam.CigarHardClipped {
			total += op.Len()
		} else {
			break
		}
	}
	return total
}
func FivePrimeClipDistance(record *sam.Record) int {
	if IsReversedRead(record) {
		return RightClipDistance(record)
	}
	return LeftClipDistance(record)
}
func UnclippedStart(record *sam.Record) int {
	return record.Start() - LeftClipDistance(record)
}
func UnclippedEnd(record *sam.Record) int {
	unclippedEnd := record.End() + RightClipDistance(record) - 1
	if unclippedEnd < record.Start() {
		panic(fmt.Sprintf("unclippedEnd is less than start: %d < %d for %s",
			unclippedEnd, record.Start(), record.Name))
	}
	return unclippedEnd
}
func UnclippedFivePrimePosition(record *sam.Record) int {
	if IsReversedRead(record) {
		return UnclippedEnd(record)
	}
	return UnclippedStart(record)
}
func indexAtPos(record *sam.Record, pos int) (int, bool) {
	if pos < record.Start() || pos >= record.End() {
		return -1, false
	}
	refPos := record.Pos
	seqPos := 0
	for _, co := range record.Cigar {
		t := co.Type()
		if t == sam.CigarBack {
			panic("No support for CigarBack")
		}
		consume := t.Consumes()
		lenref := co.Len()
		if refPos <= pos && refPos+lenref > pos {
			if consume.Query == 1 && consume.Reference == 1 {
				return seqPos + (pos - refPos), true
			} else if consume.Query == 1 {
				seqPos += lenref
			} else if consume.Reference == 1 {
				return -1, true  
			}
		} else {
			if consume.Query == 1 {
				seqPos += lenref
			}
			if consume.Reference == 1 {
				refPos += lenref
			}
		}
	}
	panic("Unexpected exit of loop")
	return -1, false
}