// Copyright ©2013 The bíogo Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package bam

import (
	"bytes"
	"compress/gzip"
	"flag"
	"fmt"
	"io"
	"os"
	"reflect"
	"testing"
	"time"

	"github.com/biogo/hts/bgzf"
	"github.com/biogo/hts/bgzf/index"
	"github.com/biogo/hts/internal"
	"github.com/biogo/hts/sam"

	"gopkg.in/check.v1"
)

var (
	bam    = flag.Bool("bam", false, "output failing bam data for inspection")
	allbam = flag.Bool("allbam", false, "output all bam data for inspection")
)

type failure bool

func (f failure) String() string {
	if f {
		return "fail"
	}
	return "ok"
}

func clonedRefs(refs []*sam.Reference) []*sam.Reference {
	c := make([]*sam.Reference, len(refs))
	for i, r := range refs {
		c[i] = r.Clone()
	}
	return c
}

func clonedRGs(rgs []*sam.ReadGroup) []*sam.ReadGroup {
	c := make([]*sam.ReadGroup, len(rgs))
	for i, r := range rgs {
		c[i] = r.Clone()
	}
	return c
}

func clonedProgs(progs []*sam.Program) []*sam.Program {
	c := make([]*sam.Program, len(progs))
	for i, p := range progs {
		c[i] = p.Clone()
	}
	return c
}

func Test(t *testing.T) { check.TestingT(t) }

type S struct{}

var _ = check.Suite(&S{})

const maxInt = int(^uint(0) >> 1)

var (
	file   = flag.String("bench.file", "", "file to read for benchmarking")
	findex = flag.String("bench.index", "", "index file to read for benchmarking")
	conc   = flag.Int("conc", 1, "sets the level of concurrency for compression")
)

func (s *S) TestRead(c *check.C) {
	for i, t := range []struct {
		in     []byte
		header *sam.Header
		lines  int
	}{
		{
			in:     bamHG00096_1000,
			header: headerHG00096_1000,
			lines:  1000,
		},
	} {
		for omit := None; omit <= AllVariableLengthData; omit++ {
			br, err := NewReader(bytes.NewBuffer(t.in), *conc)
			c.Assert(err, check.Equals, nil)

			c.Check(br.Header().Version, check.Equals, t.header.Version)
			c.Check(br.Header().SortOrder, check.Equals, t.header.SortOrder)
			c.Check(br.Header().GroupOrder, check.Equals, t.header.GroupOrder)
			c.Check(br.Header().Comments, check.DeepEquals, t.header.Comments)
			c.Check(clonedRefs(br.Header().Refs()), check.DeepEquals, clonedRefs(t.header.Refs()))
			c.Check(clonedRGs(br.Header().RGs()), check.DeepEquals, clonedRGs(t.header.RGs()))
			c.Check(clonedProgs(br.Header().Progs()), check.DeepEquals, clonedProgs(t.header.Progs()))

			var lines int
			for {
				_, err := br.Read()
				if err != nil {
					c.Assert(err, check.Equals, io.EOF)
					break
				}
				lines++
			}
			c.Check(lines, check.Equals, t.lines,
				check.Commentf("got:%d expected:%d when omit=%d", lines, t.lines, omit))
			if ok := reflect.DeepEqual(br.Header(), t.header) && lines == t.lines; *bam && !ok || *allbam {
				bf, err := os.Create(fmt.Sprintf("read-%d-%s.bam", i, failure(!ok)))
				c.Assert(err, check.Equals, nil)
				bf.Write(t.in)
				bf.Close()
			}
		}
	}
}

func headerText(h *sam.Header) []byte {
	b, _ := h.MarshalText()
	return b
}

func (s *S) TestRoundTrip(c *check.C) {
	for i, t := range []struct {
		in     []byte
		header *sam.Header
		conc   int
		lines  int
	}{
		{
			in:     bamHG00096_1000,
			header: headerHG00096_1000,
			conc:   2,
			lines:  1000,
		},
	} {
		br, err := NewReader(bytes.NewBuffer(t.in), *conc)
		c.Assert(err, check.Equals, nil)

		var buf bytes.Buffer
		bw, err := NewWriter(&buf, br.Header().Clone(), t.conc)
		for {
			r, err := br.Read()
			if err != nil {
				c.Assert(err, check.Equals, io.EOF)
				break
			}
			bw.Write(r)
		}
		c.Assert(bw.Close(), check.Equals, nil)

		br, err = NewReader(bytes.NewBuffer(t.in), *conc)
		c.Assert(err, check.Equals, nil)
		brr, err := NewReader(&buf, *conc)
		c.Assert(err, check.Equals, nil)
		c.Check(headerText(brr.Header()), check.DeepEquals, headerText(br.Header()))
		c.Check(brr.Header(), check.DeepEquals, br.Header())
		if !reflect.DeepEqual(brr.Header(), br.Header()) {
			c.Check(brr.Header().Refs(), check.DeepEquals, br.Header().Refs())
			c.Check(brr.Header().RGs(), check.DeepEquals, br.Header().RGs())
			c.Check(brr.Header().Progs(), check.DeepEquals, br.Header().Progs())
			c.Check(brr.Header().Comments, check.DeepEquals, br.Header().Comments)
		}
		allOK := true
		for {
			r, err := br.Read()
			if err != nil {
				c.Assert(err, check.Equals, io.EOF)
			}
			rr, err := brr.Read()
			if err != nil {
				c.Assert(err, check.Equals, io.EOF)
				break
			}
			c.Check(rr, check.DeepEquals, r)
			if !reflect.DeepEqual(rr, r) {
				allOK = false
			}
		}
		if ok := reflect.DeepEqual(br.Header(), brr.Header()) && allOK; *bam && !ok || *allbam {
			bf, err := os.Create(fmt.Sprintf("roundtrip-%d-%s.bam", i, failure(!ok)))
			c.Assert(err, check.Equals, nil)
			bf.Write(t.in)
			bf.Close()
		}
	}
}

func BenchmarkRead(b *testing.B) {
	if *file == "" {
		b.Skip("no file specified")
	}
	b.StopTimer()
	f, err := os.Open(*file)
	if err != nil {
		b.Fatalf("Open failed: %v", err)
	}
	br, err := NewReader(f, *conc)
	if err != nil {
		b.Fatalf("NewReader failed: %v", err)
	}
	b.StartTimer()

	for i := 0; i < b.N; i++ {
		for {
			_, err := br.Read()
			if err != nil {
				break
			}
		}
	}
	f.Close()
}

func BenchmarkReadIndex(b *testing.B) {
	if *findex == "" {
		b.Skip("no index file specified")
	}
	buf, err := os.ReadFile(*findex)
	if err != nil {
		b.Fatalf("Index read failed: %v", err)
	}
	r := bytes.NewReader(buf)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		r.Seek(0, io.SeekStart)
		_, err = ReadIndex(r)
		if err != nil {
			b.Fatal(err)
		}
	}
}

func BenchmarkReadCoreAndSeq(b *testing.B) {
	if *file == "" {
		b.Skip("no file specified")
	}
	b.StopTimer()
	f, err := os.Open(*file)
	if err != nil {
		b.Fatalf("Open failed: %v", err)
	}
	br, err := NewReader(f, *conc)
	if err != nil {
		b.Fatalf("NewReader failed: %v", err)
	}
	b.StartTimer()

	br.Omit(AuxTags)
	for i := 0; i < b.N; i++ {
		for {
			_, err := br.Read()
			if err != nil {
				break
			}
		}
	}
	f.Close()
}

func BenchmarkReadCoreOnly(b *testing.B) {
	if *file == "" {
		b.Skip("no file specified")
	}
	b.StopTimer()
	f, err := os.Open(*file)
	if err != nil {
		b.Fatalf("Open failed: %v", err)
	}
	br, err := NewReader(f, *conc)
	if err != nil {
		b.Fatalf("NewReader failed: %v", err)
	}
	b.StartTimer()

	br.Omit(AllVariableLengthData)
	for i := 0; i < b.N; i++ {
		for {
			_, err := br.Read()
			if err != nil {
				break
			}
		}
	}
	f.Close()
}

func BenchmarkWrite(b *testing.B) {
	b.StopTimer()
	br, err := NewReader(bytes.NewReader(bamHG00096_1000), *conc)
	if err != nil {
		b.Fatalf("NewReader failed: %v", err)
	}
	r, err := br.Read()
	if err != nil {
		b.Fatalf("Read failed: %v", err)
	}
	bw, err := NewWriter(io.Discard, br.Header().Clone(), *conc)
	if err != nil {
		b.Fatalf("NewWriter failed: %v", err)
	}
	b.StartTimer()

	for i := 0; i < b.N; i++ {
		err = bw.Write(r)
		if err != nil {
			b.Fatalf("Write failed: %v", err)
		}
	}
}

func BenchmarkReadFile(b *testing.B) {
	if *file == "" {
		b.Skip("no file specified")
	}

	for i := 0; i < b.N; i++ {
		f, err := os.Open(*file)
		if err != nil {
			b.Fatalf("Open failed: %v", err)
		}
		br, err := NewReader(f, *conc)
		if err != nil {
			b.Fatalf("NewReader failed: %v", err)
		}
		for {
			_, err := br.Read()
			if err != nil {
				break
			}
		}
		f.Close()
	}
}

// The is to comparable to `time samtools view -b $file > /dev/null'.
func BenchmarkRoundtripFile(b *testing.B) {
	if *file == "" {
		b.Skip("no file specified")
	}

	for i := 0; i < b.N; i++ {
		f, err := os.Open(*file)
		if err != nil {
			b.Fatalf("Open failed: %v", err)
		}
		br, err := NewReader(f, *conc)
		if err != nil {
			b.Fatalf("NewReader failed: %v", err)
		}
		bw, err := NewWriter(io.Discard, br.Header().Clone(), *conc)
		if err != nil {
			b.Fatalf("NewWriter failed: %v", err)
		}
		for {
			r, err := br.Read()
			if err != nil {
				break
			}
			err = bw.Write(r)
			if err != nil {
				b.Fatalf("Write failed: %v", err)
			}
		}
		f.Close()
	}
}

func (s *S) TestSpecExamples(c *check.C) {
	br, err := NewReader(bytes.NewReader(specExamples.data), *conc)
	c.Assert(err, check.Equals, nil)
	bh := br.Header()
	c.Check(bh.Version, check.Equals, specExamples.header.Version)
	c.Check(bh.SortOrder, check.Equals, specExamples.header.SortOrder)
	c.Check(bh.GroupOrder, check.Equals, specExamples.header.GroupOrder)
	c.Check(bh.Comments, check.DeepEquals, specExamples.header.Comments)
	for i, expect := range specExamples.records {
		r, err := br.Read()
		if err != nil {
			c.Errorf("Unexpected early error: %v", err)
		}
		c.Check(r.Name, check.Equals, expect.Name)
		c.Check(r.Pos, check.Equals, expect.Pos) // Zero-based here.
		c.Check(r.Flags, check.Equals, expect.Flags)
		if r.Flags&sam.Unmapped == 0 {
			c.Check(r.Ref, check.Not(check.Equals), nil)
			if r.Ref != nil {
				c.Check(r.Ref.Name(), check.Equals, bh.Refs()[0].Name())
			}
		} else {
			c.Check(r.Ref, check.Equals, nil)
		}
		c.Check(r.MatePos, check.Equals, expect.MatePos) // Zero-based here.
		c.Check(r.Cigar, check.DeepEquals, expect.Cigar)
		c.Check(r.Cigar.IsValid(r.Seq.Length), check.Equals, true)
		c.Check(r.TempLen, check.Equals, expect.TempLen)
		c.Check(r.Seq, check.DeepEquals, expect.Seq, check.Commentf("got:%q expected:%q", r.Seq.Expand(), expect.Seq.Expand()))
		c.Check(r.Qual, check.DeepEquals, expect.Qual) // No valid qualities here.
		c.Check(r.End(), check.Equals, specExamples.readEnds[i], check.Commentf("unexpected end position for %q at %v, got:%d expected:%d", r.Name, r.Pos, r.End(), specExamples.readEnds[i]))
		c.Check(r.AuxFields, check.DeepEquals, expect.AuxFields)

		parsedCigar, err := sam.ParseCigar([]byte(specExamples.cigars[i]))
		c.Check(err, check.Equals, nil)
		c.Check(parsedCigar, check.DeepEquals, expect.Cigar)

		// In all the examples the last base of the read and the last
		// base of the ref are valid, so we can check this.
		expSeq := r.Seq.Expand()
		c.Check(specExamples.ref[r.End()-1], check.Equals, expSeq[len(expSeq)-1])
	}
}

func (s *S) TestSpecExamplesIterator(c *check.C) {
	br, err := NewReader(bytes.NewReader(specExamples.data), *conc)
	c.Assert(err, check.Equals, nil)
	it, err := NewIterator(br, nil)
	c.Assert(err, check.Equals, nil)
	i := 0
	for ; it.Next(); i++ {
		expect := specExamples.records[i]
		r := it.Record()
		c.Check(r.Name, check.Equals, expect.Name)
		c.Check(r.Pos, check.Equals, expect.Pos)
	}
	c.Assert(i, check.Equals, len(specExamples.records))
	c.Assert(it.Error(), check.Equals, nil)
}

func mustAux(a sam.Aux, err error) sam.Aux {
	if err != nil {
		panic(err)
	}
	return a
}

// The following data is the BAM file created from the following SAM using:
//
//	samtools view -Sb spec-example.sam > spec-example.bam
//
//	@HD	VN:1.5	SO:coordinate
//	@SQ	SN:ref	LN:45
//	@CO	--------------------------------------------------------
//	@CO	Coor     12345678901234  5678901234567890123456789012345
//	@CO	ref      AGCATGTTAGATAA**GATAGCTGTGCTAGTAGGCAGTCAGCGCCAT
//	@CO	--------------------------------------------------------
//	@CO	+r001/1        TTAGATAAAGGATA*CTG
//	@CO	+r002         aaaAGATAA*GGATA
//	@CO	+r003       gcctaAGCTAA
//	@CO	+r004                     ATAGCT..............TCAGC
//	@CO	-r003                            ttagctTAGGC
//	@CO	-r001/2                                        CAGCGGCAT
//	@CO	--------------------------------------------------------
//	r001	99	ref	7	30	8M2I4M1D3M	=	37	39	TTAGATAAAGGATACTG	*
//	r002	0	ref	9	30	3S6M1P1I4M	*	0	0	AAAAGATAAGGATA	*
//	r003	0	ref	9	30	5S6M	*	0	0	GCCTAAGCTAA	*	SA:Z:ref,29,-,6H5M,17,0;
//	r004	0	ref	16	30	6M14N5M	*	0	0	ATAGCTTCAGC	*
//	r003	2064	ref	29	17	6H5M	*	0	0	TAGGC	*	SA:Z:ref,9,+,5S6M,30,1;
//	r001	147	ref	37	30	9M	=	7	-39	CAGCGGCAT	*	NM:i:1
var specExamples = struct {
	ref      string
	data     []byte
	header   sam.Header
	records  []*sam.Record
	cigars   []string
	readEnds []int
}{
	ref: "AGCATGTTAGATAAGATAGCTGTGCTAGTAGGCAGTCAGCGCCAT",
	data: []byte{
		0x1f, 0x8b, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00,
		0x00, 0xff, 0x06, 0x00, 0x42, 0x43, 0x02, 0x00,
		0xf7, 0x00, 0xa5, 0x51, 0xcb, 0x0a, 0xc2, 0x30,
		0x10, 0x8c, 0xe2, 0x25, 0xfd, 0x8a, 0x9c, 0x2b,
		0x6a, 0x53, 0x5b, 0x1f, 0x3d, 0x75, 0x8d, 0x10,
		0x0f, 0x5a, 0x91, 0x06, 0xef, 0xa1, 0x56, 0xf1,
		0xa2, 0x50, 0xf2, 0xff, 0xb8, 0x4d, 0x5f, 0x0a,
		0xc5, 0x83, 0x0e, 0x49, 0x76, 0xc9, 0xce, 0x64,
		0x1f, 0xd9, 0xc0, 0x61, 0x90, 0x0f, 0x09, 0x89,
		0x77, 0x5b, 0x7a, 0x4e, 0x22, 0x3e, 0x0d, 0x69,
		0x7a, 0x8c, 0xb2, 0xe7, 0xb3, 0xb8, 0xdc, 0x1f,
		0xda, 0xe4, 0x4e, 0x9c, 0x9e, 0x68, 0x9a, 0x44,
		0x45, 0x7e, 0xa5, 0xfb, 0x24, 0x0a, 0x42, 0x27,
		0x16, 0x47, 0x3a, 0xf9, 0x11, 0x56, 0x2c, 0xf0,
		0x71, 0x56, 0x82, 0xfb, 0xf3, 0x20, 0x5c, 0x2c,
		0x57, 0x6b, 0xaf, 0xf4, 0x18, 0xeb, 0xfc, 0x5e,
		0xcf, 0x8a, 0xb1, 0x0e, 0xab, 0x65, 0x20, 0x05,
		0x28, 0xa9, 0x14, 0x48, 0x50, 0x00, 0xae, 0x5b,
		0x1a, 0x29, 0xf0, 0x06, 0x0f, 0x90, 0xb8, 0x30,
		0x2e, 0x15, 0x6e, 0x21, 0x05, 0x32, 0xff, 0x2f,
		0x7b, 0x5c, 0x78, 0x1e, 0x9f, 0x71, 0x56, 0xa3,
		0xc9, 0x8c, 0x89, 0xd0, 0xb8, 0x98, 0xb9, 0x65,
		0xf9, 0x0d, 0x87, 0x69, 0xad, 0xeb, 0xfa, 0x2c,
		0xab, 0x65, 0xcc, 0xeb, 0xf8, 0x2d, 0xcb, 0x8c,
		0x2e, 0xcb, 0x86, 0x2e, 0x16, 0xb0, 0x3e, 0x54,
		0xdd, 0x4d, 0x3f, 0x60, 0xbb, 0xab, 0x1a, 0x7b,
		0x7b, 0xb3, 0x17, 0xc6, 0xe8, 0x5b, 0x66, 0xec,
		0x54, 0x5a, 0x01, 0x9f, 0xf9, 0xdf, 0x24, 0xef,
		0xb0, 0x63, 0x94, 0x7f, 0x8f, 0x71, 0x40, 0x08,
		0x19, 0xe1, 0xc6, 0x4f, 0x24, 0x13, 0xb4, 0x2f,
		0x02, 0xfe, 0x37, 0xdf, 0x7d, 0x02, 0x00, 0x00,
		0x1f, 0x8b, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00,
		0x00, 0xff, 0x06, 0x00, 0x42, 0x43, 0x02, 0x00,
		0x01, 0x01, 0x0b, 0x66, 0x80, 0x00, 0x36, 0x20,
		0x66, 0x95, 0xf3, 0x14, 0x62, 0x65, 0x48, 0x66,
		0x10, 0x84, 0x8a, 0xa9, 0x00, 0xb1, 0x3a, 0x10,
		0x17, 0x19, 0x18, 0x18, 0x32, 0x34, 0x00, 0x19,
		0x8a, 0x40, 0xec, 0x00, 0xc4, 0x42, 0x40, 0x6c,
		0x00, 0xc4, 0x1d, 0x22, 0x12, 0x82, 0x22, 0x8e,
		0x8d, 0x1a, 0x0e, 0xff, 0xd1, 0x81, 0x1f, 0xd4,
		0x08, 0x0e, 0xb8, 0xb1, 0x0c, 0x0c, 0x7c, 0x40,
		0x0c, 0x93, 0x67, 0x80, 0x18, 0x6b, 0xc4, 0x60,
		0x02, 0x64, 0x24, 0x00, 0xb1, 0x18, 0x10, 0x0b,
		0x42, 0x8d, 0x17, 0x14, 0x74, 0x6c, 0x04, 0x1a,
		0x8b, 0x6a, 0x62, 0x28, 0x9a, 0x89, 0x4c, 0x40,
		0x9a, 0x1b, 0xd3, 0x44, 0x63, 0x86, 0x10, 0xa8,
		0x89, 0x4e, 0x1a, 0x82, 0x4e, 0x8d, 0x02, 0x48,
		0x26, 0x04, 0x3b, 0x46, 0x15, 0xa5, 0xa6, 0xe9,
		0x18, 0x59, 0xea, 0xe8, 0xea, 0x98, 0x79, 0x98,
		0xfa, 0xea, 0x18, 0x9a, 0xeb, 0x18, 0x58, 0x33,
		0x38, 0x41, 0x0d, 0xe6, 0x87, 0x1a, 0xcc, 0x8c,
		0xdd, 0x60, 0x13, 0xb0, 0xa1, 0x8f, 0x81, 0x38,
		0x00, 0x88, 0x25, 0x44, 0x34, 0x9a, 0x44, 0x14,
		0x90, 0x0c, 0xf7, 0x86, 0x9a, 0x22, 0x03, 0x32,
		0x45, 0x10, 0xe4, 0x3c, 0x01, 0x0e, 0x56, 0x6c,
		0xce, 0x4b, 0x85, 0x9a, 0xd0, 0xe8, 0xa2, 0x80,
		0xec, 0x2a, 0x4b, 0x1d, 0x6d, 0x1d, 0xd3, 0x60,
		0x33, 0x5f, 0x1d, 0x63, 0x03, 0x1d, 0x43, 0x6b,
		0x06, 0x6b, 0xa4, 0x28, 0x00, 0x39, 0x8a, 0x91,
		0x61, 0x32, 0x03, 0x27, 0x52, 0x54, 0xdd, 0x04,
		0xea, 0x04, 0x47, 0xcb, 0x04, 0x50, 0xb4, 0x38,
		0xb9, 0x28, 0x36, 0x20, 0x82, 0xde, 0xd7, 0x99,
		0x11, 0x00, 0x9e, 0x55, 0xee, 0x4f, 0xd6, 0x01,
		0x00, 0x00, 0x1f, 0x8b, 0x08, 0x04, 0x00, 0x00,
		0x00, 0x00, 0x00, 0xff, 0x06, 0x00, 0x42, 0x43,
		0x02, 0x00, 0x1b, 0x00, 0x03, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	},
	header: sam.Header{
		Version:    "1.5",
		SortOrder:  sam.Coordinate,
		GroupOrder: sam.GroupUnspecified,
		Comments: []string{
			"--------------------------------------------------------",
			"Coor     12345678901234  5678901234567890123456789012345",
			"ref      AGCATGTTAGATAA**GATAGCTGTGCTAGTAGGCAGTCAGCGCCAT",
			"--------------------------------------------------------",
			"+r001/1        TTAGATAAAGGATA*CTG",
			"+r002         aaaAGATAA*GGATA",
			"+r003       gcctaAGCTAA",
			"+r004                     ATAGCT..............TCAGC",
			"-r003                            ttagctTAGGC",
			"-r001/2                                        CAGCGGCAT",
			"--------------------------------------------------------",
		},
	},
	records: []*sam.Record{
		{
			Name: "r001",
			Pos:  6,
			MapQ: 30,
			Cigar: sam.Cigar{
				sam.NewCigarOp(sam.CigarMatch, 8),
				sam.NewCigarOp(sam.CigarInsertion, 2),
				sam.NewCigarOp(sam.CigarMatch, 4),
				sam.NewCigarOp(sam.CigarDeletion, 1),
				sam.NewCigarOp(sam.CigarMatch, 3),
			},
			Flags:   sam.Paired | sam.ProperPair | sam.MateReverse | sam.Read1,
			MatePos: 36,
			TempLen: 39,
			Seq:     sam.NewSeq([]byte("TTAGATAAAGGATACTG")),
			Qual:    []uint8{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
		},
		{
			Name: "r002",
			Pos:  8,
			MapQ: 30,
			Cigar: sam.Cigar{
				sam.NewCigarOp(sam.CigarSoftClipped, 3),
				sam.NewCigarOp(sam.CigarMatch, 6),
				sam.NewCigarOp(sam.CigarPadded, 1),
				sam.NewCigarOp(sam.CigarInsertion, 1),
				sam.NewCigarOp(sam.CigarMatch, 4),
			},
			MatePos: -1,
			TempLen: 0,
			Seq:     sam.NewSeq([]byte("AAAAGATAAGGATA")),
			Qual:    []uint8{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
		},
		{
			Name: "r003",
			Pos:  8,
			MapQ: 30,
			Cigar: sam.Cigar{
				sam.NewCigarOp(sam.CigarSoftClipped, 5),
				sam.NewCigarOp(sam.CigarMatch, 6),
			},
			MatePos: -1,
			TempLen: 0,
			Seq:     sam.NewSeq([]byte("GCCTAAGCTAA")),
			Qual:    []uint8{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
			AuxFields: []sam.Aux{
				mustAux(sam.NewAux(sam.NewTag("SA"), "ref,29,-,6H5M,17,0;")),
			},
		},
		{
			Name: "r004",
			Pos:  15,
			MapQ: 30,
			Cigar: sam.Cigar{
				sam.NewCigarOp(sam.CigarMatch, 6),
				sam.NewCigarOp(sam.CigarSkipped, 14),
				sam.NewCigarOp(sam.CigarMatch, 5),
			},
			MatePos: -1,
			TempLen: 0,
			Seq:     sam.NewSeq([]byte("ATAGCTTCAGC")),
			Qual:    []uint8{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
		},
		{
			Name: "r003",
			Pos:  28,
			MapQ: 17,
			Cigar: sam.Cigar{
				sam.NewCigarOp(sam.CigarHardClipped, 6),
				sam.NewCigarOp(sam.CigarMatch, 5),
			},
			Flags:   sam.Reverse | sam.Supplementary,
			MatePos: -1,
			TempLen: 0,
			Seq:     sam.NewSeq([]byte("TAGGC")),
			Qual:    []uint8{0xff, 0xff, 0xff, 0xff, 0xff},
			AuxFields: []sam.Aux{
				mustAux(sam.NewAux(sam.NewTag("SA"), "ref,9,+,5S6M,30,1;")),
			},
		},
		{
			Name: "r001",
			Pos:  36,
			MapQ: 30,
			Cigar: sam.Cigar{
				sam.NewCigarOp(sam.CigarMatch, 9),
			},
			Flags:   sam.Paired | sam.ProperPair | sam.Reverse | sam.Read2,
			MatePos: 6,
			TempLen: -39,
			Seq:     sam.NewSeq([]byte("CAGCGGCAT")),
			Qual:    []uint8{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
			AuxFields: []sam.Aux{
				mustAux(sam.NewAux(sam.NewTag("NM"), uint(1))),
			},
		},
	},
	cigars: []string{
		"8M2I4M1D3M",
		"3S6M1P1I4M",
		"5S6M",
		"6M14N5M",
		"6H5M",
		"9M",
	},
	// These coordinates are all open (and zero-based) so that
	// a slice of the reference doesn't need any alteration.
	readEnds: []int{
		22,
		18,
		14,
		40,
		33,
		45,
	},
}

func (s *S) TestIssue3(c *check.C) {
	for _, test := range issue3 {
		br, err := NewReader(bytes.NewReader(test), *conc)
		c.Assert(err, check.Equals, nil, check.Commentf("Failed to open BAM"))

		for {
			_, err := br.Read()
			if err != nil {
				c.Check(err, check.Equals, io.EOF)
				break
			}
		}
	}
}

var issue3 = [][]byte{
	// Specific case posted at https://code.google.com/p/biogo/issues/detail?id=3#c6
	// edited to a minimal reproducer.
	{
		0x1f, 0x8b, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00,
		0x00, 0xff, 0x06, 0x00, 0x42, 0x43, 0x02, 0x00,
		0x61, 0x00, 0x73, 0x72, 0xf4, 0x65, 0x34, 0x62,
		0x60, 0x60, 0x70, 0xf0, 0x70, 0xe1, 0x0c, 0xf3,
		0xb3, 0x32, 0xd4, 0x33, 0xe0, 0x0c, 0xf6, 0xb7,
		0x4a, 0xce, 0xcf, 0x2f, 0x4a, 0xc9, 0xcc, 0x4b,
		0x2c, 0x49, 0xe5, 0x72, 0x08, 0x0e, 0xe4, 0x0c,
		0xf6, 0xb3, 0x8a, 0x2a, 0xb3, 0x8c, 0xf7, 0x73,
		0x34, 0x34, 0xe0, 0xf4, 0xf1, 0xb3, 0x32, 0xb1,
		0x30, 0x30, 0x33, 0xe2, 0x62, 0x04, 0xea, 0xe2,
		0x04, 0x62, 0x98, 0x0c, 0xc3, 0xbe, 0xdd, 0x0c,
		0x0c, 0x00, 0xaa, 0x89, 0xab, 0xc2, 0x4f, 0x00,
		0x00, 0x00, 0x1f, 0x8b, 0x08, 0x04, 0x00, 0x00,
		0x00, 0x00, 0x00, 0xff, 0x06, 0x00, 0x42, 0x43,
		0x02, 0x00, 0xa2, 0x00, 0x9b, 0xc5, 0x00, 0x01,
		0xb3, 0xa5, 0x19, 0x18, 0xf8, 0x19, 0x3c, 0x85,
		0x18, 0x19, 0x18, 0x18, 0x25, 0x81, 0xfc, 0xff,
		0x50, 0x00, 0x92, 0x33, 0x34, 0x28, 0x30, 0x30,
		0xcc, 0x4a, 0x4b, 0xce, 0x53, 0x76, 0x76, 0x76,
		0x0f, 0x61, 0x98, 0x00, 0x54, 0x23, 0xd4, 0x01,
		0x05, 0x4a, 0x1d, 0x1d, 0x0d, 0xaa, 0xca, 0xca,
		0xca, 0x8a, 0xaa, 0xea, 0xea, 0xea, 0x1a, 0x9a,
		0x9a, 0x9a, 0x1a, 0x40, 0x52, 0x55, 0x59, 0x51,
		0x45, 0x55, 0x55, 0x45, 0xc9, 0x31, 0x38, 0xf9,
		0x77, 0x84, 0x9f, 0x33, 0x43, 0x84, 0xaf, 0x33,
		0x63, 0x84, 0x3f, 0x90, 0x76, 0x77, 0x66, 0xf0,
		0x03, 0xb2, 0x7d, 0x5d, 0xa2, 0x8c, 0x8c, 0x1d,
		0x0d, 0x19, 0x22, 0x43, 0xa2, 0x42, 0x43, 0x19,
		0xfc, 0x3c, 0x9c, 0x45, 0x9c, 0x9d, 0xa3, 0x92,
		0x33, 0x8a, 0x0c, 0x19, 0x9c, 0x03, 0x3c, 0xf7,
		0x9a, 0x3f, 0x67, 0x8a, 0x08, 0x76, 0xd4, 0xf6,
		0xf0, 0x74, 0x66, 0x00, 0x00, 0xa6, 0xdc, 0x77,
		0x77, 0x9e, 0x00, 0x00, 0x00, 0x1f, 0x8b, 0x08,
		0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x06,
		0x00, 0x42, 0x43, 0x02, 0x00, 0x1b, 0x00, 0x03,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00,
	},

	// More general testing: sequence lengths range from 19 to 26 inclusive.
	{
		0x1f, 0x8b, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00,
		0x00, 0xff, 0x06, 0x00, 0x42, 0x43, 0x02, 0x00,
		0x61, 0x00, 0x73, 0x72, 0xf4, 0x65, 0x34, 0x62,
		0x60, 0x60, 0x70, 0xf0, 0x70, 0xe1, 0x0c, 0xf3,
		0xb3, 0x32, 0xd4, 0x33, 0xe0, 0x0c, 0xf6, 0xb7,
		0x4a, 0xce, 0xcf, 0x2f, 0x4a, 0xc9, 0xcc, 0x4b,
		0x2c, 0x49, 0xe5, 0x72, 0x08, 0x0e, 0xe4, 0x0c,
		0xf6, 0xb3, 0x8a, 0x2a, 0xb3, 0x8c, 0xf7, 0x73,
		0x34, 0x34, 0xe0, 0xf4, 0xf1, 0xb3, 0x32, 0xb1,
		0x30, 0x30, 0x33, 0xe2, 0x62, 0x04, 0xea, 0xe2,
		0x04, 0x62, 0x98, 0x0c, 0xc3, 0xbe, 0xdd, 0x0c,
		0x0c, 0x00, 0xaa, 0x89, 0xab, 0xc2, 0x4f, 0x00,
		0x00, 0x00, 0x1f, 0x8b, 0x08, 0x04, 0x00, 0x00,
		0x00, 0x00, 0x00, 0xff, 0x06, 0x00, 0x42, 0x43,
		0x02, 0x00, 0x12, 0x01, 0x9b, 0xc8, 0x00, 0x01,
		0xb3, 0xa5, 0x19, 0x18, 0xf8, 0x19, 0x3c, 0x85,
		0x18, 0x19, 0x18, 0x18, 0x85, 0x81, 0xfc, 0xff,
		0x50, 0x00, 0x92, 0x33, 0x34, 0x28, 0x30, 0x30,
		0xcc, 0x4a, 0x4b, 0xce, 0x53, 0x76, 0x76, 0x76,
		0x0f, 0x61, 0x30, 0x00, 0xaa, 0x11, 0xea, 0x80,
		0x02, 0x05, 0x55, 0x65, 0x65, 0x65, 0x45, 0x55,
		0x75, 0x75, 0x75, 0x0d, 0x4d, 0x4d, 0x4d, 0x0d,
		0x20, 0xa9, 0xaa, 0xec, 0x18, 0x9c, 0xfc, 0x3b,
		0xc2, 0xcf, 0x99, 0x21, 0xc2, 0xd7, 0x99, 0x31,
		0xc2, 0x1f, 0x48, 0xbb, 0x3b, 0x33, 0xf8, 0x01,
		0xd9, 0xbe, 0x2e, 0x51, 0x46, 0xc6, 0x8e, 0x86,
		0x0c, 0x91, 0x21, 0x51, 0xa1, 0xa1, 0x0c, 0x7e,
		0x1e, 0xce, 0x22, 0xce, 0xce, 0x51, 0xc9, 0x19,
		0x45, 0x86, 0x0c, 0xce, 0x01, 0x9e, 0x7b, 0xcd,
		0x9f, 0x33, 0x45, 0x04, 0x3b, 0x6a, 0x7b, 0x78,
		0x3a, 0x33, 0x4c, 0xc2, 0xe2, 0x28, 0x11, 0xfc,
		0x8e, 0x72, 0x40, 0x76, 0x94, 0x12, 0x16, 0x47,
		0x29, 0x52, 0xec, 0xaa, 0x29, 0x58, 0x5c, 0x25,
		0x8a, 0xdf, 0x55, 0x01, 0x28, 0xae, 0x6a, 0xc0,
		0xe6, 0x2c, 0x15, 0x8a, 0xdd, 0x35, 0x15, 0x8b,
		0xbb, 0xc4, 0xf0, 0xbb, 0x2b, 0x01, 0xc5, 0x5d,
		0x1d, 0x58, 0xdd, 0xa5, 0x4a, 0xb1, 0xc3, 0xa6,
		0x63, 0x71, 0x98, 0x38, 0x7e, 0x87, 0x15, 0xa0,
		0x3a, 0x0c, 0x7b, 0x88, 0xa9, 0x52, 0xee, 0xb4,
		0x19, 0x58, 0x9c, 0x26, 0x81, 0xdf, 0x69, 0x0d,
		0xa8, 0x4e, 0xc3, 0x11, 0x68, 0xaa, 0x94, 0xc7,
		0xe7, 0x2c, 0x2c, 0x6e, 0x93, 0xc4, 0xef, 0xb6,
		0x09, 0x68, 0x6e, 0xc3, 0x15, 0x6e, 0x2a, 0x4a,
		0x14, 0xbb, 0x6e, 0x36, 0x16, 0xd7, 0x49, 0xe1,
		0x77, 0xdd, 0x02, 0x34, 0xd7, 0xe1, 0x0c, 0x3a,
		0x25, 0xca, 0x9d, 0x07, 0x00, 0x31, 0x91, 0x24,
		0x5a, 0xd0, 0x04, 0x00, 0x00, 0x1f, 0x8b, 0x08,
		0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x06,
		0x00, 0x42, 0x43, 0x02, 0x00, 0x1b, 0x00, 0x03,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00,
	},
}

func (s *S) TestIssue11(c *check.C) {
	for _, test := range issue11 {
		br, err := NewReader(bytes.NewReader(test.input), *conc)
		c.Check(err, check.Equals, nil)
		c.Check(br.Header().GroupOrder, check.Equals, test.expect)
	}
}

var issue11 = []struct {
	input  []byte
	expect sam.GroupOrder
}{
	{
		// 1000 genomes file hand edited to reduce number of Refs and reads;
		// no specified group ordering.
		input: []byte{
			0x1f, 0x8b, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00,
			0x00, 0xff, 0x06, 0x00, 0x42, 0x43, 0x02, 0x00,
			0xec, 0x01, 0xad, 0x92, 0xcd, 0x8e, 0xda, 0x30,
			0x10, 0xc7, 0xb3, 0xdd, 0x93, 0x79, 0x8a, 0x1c,
			0x38, 0xb4, 0x07, 0x1c, 0xe7, 0xc3, 0x81, 0x58,
			0xaa, 0xc4, 0x97, 0x04, 0x48, 0x21, 0x9b, 0x12,
			0xd1, 0x6a, 0x4f, 0x91, 0x93, 0xd8, 0xc1, 0x5a,
			0xe3, 0x00, 0x0e, 0xd0, 0xf6, 0x0d, 0xfa, 0xc4,
			0xbd, 0x55, 0x35, 0xd1, 0xb6, 0x5d, 0xb5, 0x3d,
			0xee, 0xc1, 0x23, 0xfb, 0x3f, 0x33, 0xbf, 0x19,
			0xdb, 0x33, 0x9d, 0xac, 0xef, 0xb2, 0x7b, 0xcb,
			0x1a, 0x2f, 0xe7, 0xe0, 0x63, 0x42, 0x5c, 0x88,
			0x40, 0xf6, 0x40, 0xca, 0xa6, 0x39, 0x55, 0x42,
			0xd1, 0x96, 0xf5, 0xc6, 0xd9, 0x07, 0x90, 0x25,
			0xe4, 0x11, 0xc4, 0x09, 0xc1, 0x91, 0x3f, 0xf4,
			0x71, 0x18, 0x82, 0x35, 0x26, 0x2e, 0xa7, 0x7e,
			0x30, 0x0c, 0x86, 0x18, 0x51, 0x8e, 0xa2, 0x60,
			0x54, 0x54, 0x3c, 0x1a, 0x56, 0x98, 0x22, 0xc6,
			0xb0, 0xc7, 0xb0, 0x0b, 0xb6, 0x1b, 0xc2, 0xdb,
			0x03, 0x71, 0x1c, 0x63, 0xa1, 0x8b, 0x10, 0xaa,
			0x99, 0x6a, 0xf6, 0x4c, 0x43, 0x56, 0x08, 0x48,
			0x4b, 0x78, 0x7e, 0x72, 0x2e, 0x8d, 0x74, 0x6f,
			0x6e, 0xa7, 0x65, 0xe5, 0x4e, 0x89, 0x92, 0x4a,
			0xe7, 0xc4, 0x38, 0x3b, 0x31, 0x55, 0x32, 0xe7,
			0xb0, 0xa3, 0x9a, 0x79, 0xf9, 0x6f, 0x21, 0xa7,
			0x5a, 0xb3, 0x7d, 0x21, 0xbf, 0xe4, 0x9a, 0x1d,
			0xcf, 0x5d, 0xc8, 0x4e, 0xfb, 0xa6, 0x24, 0xe4,
			0x14, 0xd6, 0x5f, 0xc1, 0x24, 0x23, 0xc9, 0x6c,
			0xba, 0xf2, 0x87, 0x20, 0x4b, 0xc9, 0xf2, 0xbc,
			0xa7, 0xaa, 0x37, 0xde, 0x2c, 0xc0, 0x6a, 0x4e,
			0xb2, 0xcd, 0x06, 0x85, 0x5e, 0xe8, 0x07, 0x20,
			0x9e, 0x12, 0x6f, 0x14, 0xe0, 0x11, 0x0e, 0x47,
			0xd8, 0x5c, 0x74, 0x4d, 0x96, 0x0b, 0xd3, 0x58,
			0x14, 0x82, 0x74, 0x45, 0x3c, 0x14, 0x82, 0x59,
			0x42, 0x3e, 0x6d, 0x17, 0xd9, 0x0c, 0xa4, 0x31,
			0x59, 0xc5, 0xf1, 0x76, 0xbd, 0x4a, 0x26, 0x60,
			0x9e, 0x19, 0x42, 0x8a, 0x90, 0xeb, 0x45, 0xc1,
			0xdf, 0x48, 0xfc, 0xea, 0xc8, 0xc0, 0x7d, 0x0d,
			0x64, 0xda, 0x21, 0x8b, 0x2b, 0xcd, 0x85, 0xaa,
			0xd8, 0x67, 0x90, 0x26, 0xb7, 0xc3, 0xed, 0x87,
			0x11, 0xc4, 0x30, 0x1a, 0x9c, 0x5c, 0x83, 0x89,
			0x6f, 0x9a, 0xdd, 0x05, 0xd8, 0x03, 0x6a, 0x17,
			0xd7, 0x56, 0x5f, 0xed, 0xfe, 0x9f, 0x07, 0xe7,
			0x54, 0xb7, 0xf4, 0x25, 0x8b, 0x4a, 0xd5, 0x89,
			0xc7, 0x5f, 0xbc, 0x34, 0x7d, 0x51, 0xe3, 0x7f,
			0x70, 0x93, 0x61, 0x0f, 0x8e, 0xb6, 0x8b, 0xed,
			0x01, 0xb7, 0xfb, 0x9a, 0x8a, 0x9c, 0x0b, 0xc9,
			0xfe, 0x29, 0x62, 0xf7, 0x3b, 0x6c, 0xe7, 0xec,
			0x8d, 0x67, 0x0f, 0xa0, 0xff, 0xa4, 0x9a, 0xab,
			0xea, 0xc0, 0x52, 0x77, 0xf2, 0x5b, 0xfd, 0xce,
			0x7e, 0x6f, 0xbf, 0xc6, 0x44, 0xed, 0xe9, 0xe1,
			0x20, 0x54, 0x6d, 0x26, 0x4b, 0x37, 0xe7, 0x53,
			0xc9, 0xb4, 0x33, 0x89, 0x63, 0x78, 0xad, 0x35,
			0x7c, 0x2e, 0xb7, 0x17, 0xd2, 0xd8, 0x8a, 0x5d,
			0x84, 0x62, 0xf9, 0xae, 0x76, 0xa3, 0x5c, 0x32,
			0xde, 0x4e, 0xa4, 0xa8, 0x15, 0xab, 0xf2, 0xb2,
			0x91, 0x92, 0x1e, 0xb4, 0xd9, 0x55, 0xcd, 0xb9,
			0x90, 0x26, 0x42, 0xb4, 0xcf, 0x99, 0x50, 0x8b,
			0xd6, 0xb4, 0x73, 0x29, 0xb9, 0x19, 0xc8, 0xde,
			0x9d, 0x65, 0x59, 0x6f, 0xcc, 0x7a, 0xb4, 0x7e,
			0x7c, 0xff, 0x76, 0xff, 0x13, 0x01, 0xfd, 0x62,
			0x77, 0x69, 0x03, 0x00, 0x00, 0x1f, 0x8b, 0x08,
			0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x06,
			0x00, 0x42, 0x43, 0x02, 0x00, 0xcf, 0x00, 0x4d,
			0xce, 0x31, 0x6a, 0xc3, 0x30, 0x18, 0x05, 0x60,
			0x41, 0x0f, 0x50, 0xd2, 0x66, 0x30, 0x74, 0x88,
			0xab, 0x18, 0xeb, 0x59, 0xb2, 0xa5, 0xff, 0xb7,
			0xa9, 0x93, 0x8c, 0x32, 0x82, 0x6a, 0x76, 0xb6,
			0xce, 0x3d, 0x40, 0x2e, 0x90, 0x41, 0xc1, 0x83,
			0x73, 0xc1, 0xdc, 0xa4, 0x74, 0xad, 0x4b, 0xa0,
			0xf4, 0x5b, 0xde, 0xe3, 0x4d, 0xef, 0x26, 0xee,
			0xbe, 0xbe, 0xaf, 0x0f, 0xcf, 0xe2, 0x94, 0x0b,
			0x71, 0x16, 0x9f, 0xff, 0xb6, 0xdf, 0x3c, 0x8e,
			0x23, 0xf5, 0x6d, 0xdf, 0xbd, 0x59, 0x3e, 0xf4,
			0xdd, 0x6e, 0xdf, 0xb6, 0x62, 0xb8, 0xc8, 0xe8,
			0x63, 0x98, 0x57, 0x29, 0x44, 0x9f, 0xbc, 0x9c,
			0xa6, 0x4c, 0x66, 0xc5, 0x94, 0x45, 0xa4, 0x39,
			0xad, 0x83, 0x7f, 0x8a, 0x31, 0x0c, 0x4b, 0x0d,
			0x98, 0xb3, 0x20, 0xe1, 0xd7, 0xe9, 0x55, 0xe6,
			0x79, 0xfe, 0x52, 0xa0, 0xd2, 0xa5, 0xde, 0x42,
			0xab, 0x2d, 0x60, 0xea, 0x1a, 0xc6, 0x28, 0x65,
			0xa1, 0x01, 0xab, 0x6c, 0xa9, 0x50, 0x35, 0x0a,
			0x1b, 0xc7, 0xda, 0xb0, 0x61, 0xaa, 0x1d, 0xa1,
			0xd4, 0xa6, 0x65, 0x36, 0x64, 0xa0, 0x1c, 0x3b,
			0x66, 0x22, 0x76, 0x8e, 0xc8, 0x71, 0x45, 0x0b,
			0x0b, 0xba, 0xb3, 0x4d, 0xa3, 0xf1, 0x38, 0xbe,
			0x7f, 0xfc, 0x7d, 0x15, 0x3f, 0xf2, 0x15, 0x80,
			0x8f, 0xda, 0x00, 0x00, 0x00, 0x1f, 0x8b, 0x08,
			0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x06,
			0x00, 0x42, 0x43, 0x02, 0x00, 0x1b, 0x00, 0x03,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00,
		},
		expect: sam.GroupUnspecified,
	},
	{
		input: []byte{
			0x1f, 0x8b, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00,
			0x00, 0xff, 0x06, 0x00, 0x42, 0x43, 0x02, 0x00,
			0xf3, 0x01, 0xad, 0x92, 0xcd, 0x8e, 0xda, 0x30,
			0x10, 0xc7, 0xb3, 0xdd, 0x93, 0x79, 0x8a, 0x1c,
			0x38, 0xb4, 0x07, 0x1c, 0xe7, 0xc3, 0x81, 0x58,
			0xaa, 0xc4, 0x97, 0x04, 0x48, 0x21, 0xa4, 0x44,
			0xb4, 0x5a, 0xf5, 0x10, 0x39, 0x89, 0x13, 0xac,
			0x35, 0x0e, 0xe0, 0x00, 0xdb, 0xbe, 0xc1, 0x3e,
			0xf1, 0xde, 0xaa, 0x9a, 0x68, 0xdb, 0xae, 0xda,
			0x1e, 0x39, 0x78, 0x64, 0xff, 0x67, 0xe6, 0x37,
			0x63, 0x7b, 0xc6, 0xa3, 0xe5, 0xdd, 0xd7, 0x7b,
			0xc3, 0x18, 0xce, 0xa7, 0xe0, 0x73, 0x44, 0x6c,
			0x88, 0x40, 0xb2, 0x22, 0x79, 0x5d, 0x1f, 0x0b,
			0x2e, 0x69, 0xc3, 0xc0, 0x6c, 0x45, 0x64, 0x2d,
			0x59, 0x67, 0x98, 0x7c, 0x02, 0x49, 0x44, 0x1e,
			0x40, 0x18, 0x11, 0x1c, 0xb8, 0x7d, 0x17, 0xfb,
			0x3e, 0x58, 0x62, 0x62, 0x97, 0xd4, 0xf5, 0xfa,
			0x5e, 0x1f, 0x23, 0x5a, 0xa2, 0xc0, 0x1b, 0x64,
			0x45, 0x19, 0xf4, 0x0b, 0x4c, 0x11, 0x63, 0xd8,
			0x61, 0xd8, 0x06, 0x9b, 0x35, 0x29, 0x9b, 0x3d,
			0xb1, 0x2c, 0x6d, 0xa1, 0x8d, 0x10, 0xaa, 0x98,
			0xac, 0x77, 0x4c, 0x41, 0x96, 0x71, 0x48, 0x73,
			0x78, 0x7a, 0xb4, 0xce, 0xb5, 0xb0, 0xaf, 0x6e,
			0xab, 0x61, 0xf9, 0x56, 0xf2, 0x9c, 0x0a, 0xeb,
			0xc8, 0x4a, 0x76, 0x64, 0x32, 0x67, 0xd6, 0x7e,
			0x4b, 0x15, 0x73, 0xd2, 0xdf, 0x42, 0x4a, 0x95,
			0x62, 0xbb, 0x4c, 0x7c, 0x4b, 0x15, 0x3b, 0x9c,
			0xda, 0x90, 0xad, 0x72, 0x75, 0x49, 0x58, 0x52,
			0x58, 0x7d, 0x07, 0xa3, 0x84, 0x44, 0x93, 0xf1,
			0xc2, 0xed, 0x83, 0x24, 0x26, 0xf3, 0xd3, 0x8e,
			0xca, 0xce, 0x70, 0x3d, 0x03, 0x8b, 0x29, 0x49,
			0xd6, 0x6b, 0xe4, 0x3b, 0xbe, 0xeb, 0x81, 0x70,
			0x4c, 0x9c, 0x81, 0x87, 0x07, 0xd8, 0x1f, 0x60,
			0x7d, 0xe1, 0x25, 0x99, 0xcf, 0x74, 0x63, 0x81,
			0x0f, 0xe2, 0x05, 0x71, 0x90, 0x0f, 0x26, 0x11,
			0xf9, 0xb2, 0x99, 0x25, 0x13, 0x10, 0x87, 0x64,
			0x11, 0x86, 0x9b, 0xe5, 0x22, 0x1a, 0x81, 0x69,
			0xa2, 0x09, 0x31, 0x42, 0xb6, 0x13, 0x78, 0x7f,
			0x23, 0xf1, 0xcd, 0x91, 0x9e, 0x7d, 0x0b, 0x64,
			0xdc, 0x22, 0xb3, 0x0b, 0x4d, 0xb9, 0x2c, 0xd8,
			0x13, 0x88, 0xa3, 0xeb, 0xe1, 0xfa, 0xd3, 0x08,
			0x62, 0x18, 0xf4, 0x8e, 0xb6, 0xc6, 0x84, 0x57,
			0xcd, 0x6c, 0x03, 0xcc, 0x1e, 0x35, 0xb3, 0x4b,
			0xa3, 0x2e, 0x66, 0xf7, 0xcf, 0x83, 0x97, 0x54,
			0x35, 0xf4, 0x2d, 0x8b, 0x0a, 0xd9, 0x8a, 0x87,
			0x5f, 0xbc, 0x38, 0x7e, 0x53, 0xe3, 0x7f, 0x70,
			0x9d, 0x61, 0xf6, 0x0e, 0xa6, 0x8d, 0xcd, 0x5e,
			0x69, 0x76, 0x15, 0xe5, 0x69, 0xc9, 0x05, 0xfb,
			0xa7, 0x88, 0xd9, 0x6d, 0xb1, 0xad, 0xb3, 0x33,
			0x9c, 0xac, 0x40, 0xf7, 0x51, 0xd6, 0x17, 0xd9,
			0x82, 0x85, 0x6a, 0xe5, 0xf7, 0xea, 0x83, 0xf9,
			0xd1, 0xbc, 0xc5, 0x44, 0xed, 0xe8, 0x7e, 0xcf,
			0x65, 0xa5, 0x27, 0x4b, 0xd5, 0xa7, 0x63, 0xce,
			0x94, 0x35, 0x0a, 0x43, 0x78, 0xa9, 0x14, 0x7c,
			0x2d, 0xb7, 0xe3, 0x42, 0xdb, 0x82, 0x9d, 0xb9,
			0x64, 0xe9, 0xb6, 0xb2, 0x83, 0x54, 0xb0, 0xb2,
			0x19, 0x09, 0x5e, 0x49, 0x56, 0xa4, 0x79, 0x2d,
			0x04, 0xdd, 0x2b, 0xbd, 0x2b, 0xea, 0x53, 0x26,
			0x74, 0x04, 0x6f, 0x5e, 0x33, 0xa1, 0xe2, 0x8d,
			0x6e, 0xe7, 0x9c, 0x97, 0x7a, 0x20, 0x3b, 0x77,
			0x86, 0x61, 0xbc, 0xd3, 0xeb, 0xc1, 0xf8, 0xf1,
			0xf2, 0x7c, 0xff, 0x13, 0x5a, 0x38, 0x56, 0x0a,
			0x71, 0x03, 0x00, 0x00, 0x1f, 0x8b, 0x08, 0x04,
			0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x06, 0x00,
			0x42, 0x43, 0x02, 0x00, 0xcf, 0x00, 0x4d, 0xce,
			0x31, 0x6a, 0xc3, 0x30, 0x18, 0x05, 0x60, 0x41,
			0x0f, 0x50, 0xd2, 0x66, 0x30, 0x74, 0x88, 0xab,
			0x18, 0xeb, 0x59, 0xb2, 0xa5, 0xff, 0xb7, 0xa9,
			0x93, 0x8c, 0x32, 0x82, 0x6a, 0x76, 0xb6, 0xce,
			0x3d, 0x40, 0x2e, 0x90, 0x41, 0xc1, 0x83, 0x73,
			0xc1, 0xdc, 0xa4, 0x74, 0xad, 0x4b, 0xa0, 0xf4,
			0x5b, 0xde, 0xe3, 0x4d, 0xef, 0x26, 0xee, 0xbe,
			0xbe, 0xaf, 0x0f, 0xcf, 0xe2, 0x94, 0x0b, 0x71,
			0x16, 0x9f, 0xff, 0xb6, 0xdf, 0x3c, 0x8e, 0x23,
			0xf5, 0x6d, 0xdf, 0xbd, 0x59, 0x3e, 0xf4, 0xdd,
			0x6e, 0xdf, 0xb6, 0x62, 0xb8, 0xc8, 0xe8, 0x63,
			0x98, 0x57, 0x29, 0x44, 0x9f, 0xbc, 0x9c, 0xa6,
			0x4c, 0x66, 0xc5, 0x94, 0x45, 0xa4, 0x39, 0xad,
			0x83, 0x7f, 0x8a, 0x31, 0x0c, 0x4b, 0x0d, 0x98,
			0xb3, 0x20, 0xe1, 0xd7, 0xe9, 0x55, 0xe6, 0x79,
			0xfe, 0x52, 0xa0, 0xd2, 0xa5, 0xde, 0x42, 0xab,
			0x2d, 0x60, 0xea, 0x1a, 0xc6, 0x28, 0x65, 0xa1,
			0x01, 0xab, 0x6c, 0xa9, 0x50, 0x35, 0x0a, 0x1b,
			0xc7, 0xda, 0xb0, 0x61, 0xaa, 0x1d, 0xa1, 0xd4,
			0xa6, 0x65, 0x36, 0x64, 0xa0, 0x1c, 0x3b, 0x66,
			0x22, 0x76, 0x8e, 0xc8, 0x71, 0x45, 0x0b, 0x0b,
			0xba, 0xb3, 0x4d, 0xa3, 0xf1, 0x38, 0xbe, 0x7f,
			0xfc, 0x7d, 0x15, 0x3f, 0xf2, 0x15, 0x80, 0x8f,
			0xda, 0x00, 0x00, 0x00, 0x1f, 0x8b, 0x08, 0x04,
			0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x06, 0x00,
			0x42, 0x43, 0x02, 0x00, 0x1b, 0x00, 0x03, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		},
		expect: sam.GroupNone,
	},
	{
		input: []byte{
			0x1f, 0x8b, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00,
			0x00, 0xff, 0x06, 0x00, 0x42, 0x43, 0x02, 0x00,
			0xf5, 0x01, 0xad, 0x92, 0xcb, 0x8e, 0xda, 0x30,
			0x14, 0x86, 0x33, 0x9d, 0x95, 0x79, 0x8a, 0x2c,
			0x58, 0xb4, 0x0b, 0x1c, 0xe7, 0xe2, 0x40, 0x2c,
			0x55, 0xe2, 0x26, 0x01, 0x52, 0x08, 0x29, 0x11,
			0xad, 0x46, 0xaa, 0x14, 0x39, 0x89, 0x13, 0xac,
			0x31, 0x0e, 0x13, 0x07, 0xe8, 0xf4, 0x0d, 0xfa,
			0xc4, 0xdd, 0x55, 0x35, 0xd1, 0xb4, 0x1d, 0xb5,
			0x5d, 0xb2, 0xf0, 0x91, 0x7d, 0x2e, 0xdf, 0x7f,
			0x6c, 0x9f, 0xe9, 0x64, 0x7d, 0xf7, 0xf9, 0xde,
			0x30, 0xc6, 0xcb, 0x39, 0xf8, 0x18, 0x11, 0x1b,
			0x22, 0x90, 0x6c, 0x48, 0x5e, 0xd7, 0x4d, 0xc1,
			0x25, 0x6d, 0x19, 0x58, 0x6c, 0xc8, 0xd3, 0x89,
			0x35, 0xcf, 0xbd, 0x71, 0xf2, 0x01, 0x24, 0x11,
			0x79, 0x00, 0x61, 0x44, 0x70, 0xe0, 0x0e, 0x5d,
			0xec, 0xfb, 0x60, 0x8d, 0x89, 0x5d, 0x52, 0xd7,
			0x1b, 0x7a, 0x43, 0x8c, 0x68, 0x89, 0x02, 0x6f,
			0x94, 0x15, 0x65, 0x30, 0x2c, 0x30, 0x45, 0x8c,
			0x61, 0x87, 0x61, 0x1b, 0xec, 0xb6, 0xa4, 0x6c,
			0x8f, 0xc4, 0xb2, 0xb4, 0x85, 0x36, 0x42, 0xa8,
			0x62, 0xb2, 0x3e, 0x30, 0x05, 0x59, 0xc6, 0x21,
			0xcd, 0xe1, 0xe9, 0xd1, 0x3a, 0xd7, 0xc2, 0xbe,
			0x86, 0xad, 0x96, 0xe5, 0x7b, 0xc9, 0x73, 0x2a,
			0xac, 0x86, 0x95, 0xac, 0x61, 0x32, 0x67, 0xd6,
			0x71, 0x4f, 0x15, 0x73, 0xd2, 0xdf, 0x8e, 0x94,
			0x2a, 0xc5, 0x0e, 0x99, 0x78, 0x4e, 0x15, 0xd3,
			0xad, 0x5d, 0x53, 0xf6, 0xca, 0xd5, 0x92, 0xb0,
			0xa4, 0xb0, 0xfa, 0x0a, 0x26, 0x09, 0x89, 0x66,
			0xd3, 0x95, 0x3b, 0x04, 0x49, 0x4c, 0x96, 0xa7,
			0x03, 0x95, 0xbd, 0xf1, 0x76, 0x01, 0x56, 0x73,
			0x92, 0x6c, 0xb7, 0xc8, 0x77, 0x7c, 0xd7, 0x03,
			0xe1, 0x94, 0x38, 0x23, 0x0f, 0x8f, 0xb0, 0x3f,
			0xc2, 0xfa, 0xc6, 0x6b, 0xb2, 0x5c, 0xe8, 0xc6,
			0x02, 0x1f, 0xc4, 0x2b, 0xe2, 0x20, 0x1f, 0xcc,
			0x22, 0xf2, 0x69, 0xb7, 0x48, 0x66, 0x20, 0x0e,
			0xc9, 0x2a, 0x0c, 0x77, 0xeb, 0x55, 0x34, 0x01,
			0xf3, 0x44, 0x13, 0x62, 0x84, 0x6c, 0x27, 0xf0,
			0xfe, 0x46, 0xe2, 0x9b, 0x23, 0x3d, 0xfb, 0x16,
			0xc8, 0xb8, 0x43, 0x66, 0x17, 0x9a, 0x72, 0x59,
			0xb0, 0x2f, 0x20, 0x8e, 0xae, 0x87, 0xeb, 0x57,
			0x23, 0x88, 0x61, 0x30, 0x68, 0x6c, 0x8d, 0x09,
			0xaf, 0x3e, 0xb3, 0x4b, 0x30, 0x07, 0xd4, 0xcc,
			0x2e, 0xad, 0xba, 0x98, 0xfd, 0x3f, 0x0f, 0x5e,
			0x52, 0xd5, 0xd2, 0xd7, 0x2c, 0x2a, 0x64, 0xe7,
			0x7c, 0xfa, 0xc5, 0x8b, 0xe3, 0x57, 0x1a, 0xff,
			0x83, 0xeb, 0x0a, 0x73, 0xf0, 0x64, 0xda, 0xd8,
			0x1c, 0x94, 0x66, 0x5f, 0x51, 0x9e, 0x96, 0x5c,
			0xb0, 0x7f, 0x44, 0xcc, 0x7e, 0x87, 0xed, 0x82,
			0xbd, 0xf1, 0x6c, 0x03, 0xfa, 0x8f, 0xb2, 0xbe,
			0xc8, 0x0e, 0x2c, 0x54, 0xe7, 0x7e, 0xab, 0xde,
			0x99, 0xef, 0xcd, 0x5b, 0x4c, 0xd4, 0x81, 0x1e,
			0x8f, 0x5c, 0x56, 0x7a, 0xb2, 0x54, 0x7d, 0x6a,
			0x72, 0xa6, 0xac, 0x49, 0x18, 0xc2, 0x4b, 0xa5,
			0xe0, 0x8b, 0xdc, 0x81, 0x0b, 0x6d, 0x0b, 0x76,
			0xe6, 0x92, 0xa5, 0xfb, 0xca, 0x0e, 0x52, 0xc1,
			0xca, 0x76, 0x22, 0x78, 0x25, 0x59, 0x91, 0xe6,
			0xb5, 0x10, 0xf4, 0xa8, 0xf4, 0xae, 0xa8, 0x4f,
			0x99, 0xd0, 0x19, 0xbc, 0x7d, 0xa9, 0x84, 0x8a,
			0xb7, 0xba, 0x9d, 0x73, 0x5e, 0xea, 0x81, 0xec,
			0xdd, 0x19, 0x86, 0xf1, 0x46, 0xaf, 0x07, 0xe3,
			0xc7, 0xf7, 0x6f, 0xf7, 0x3f, 0x01, 0x63, 0x09,
			0x85, 0xc2, 0x72, 0x03, 0x00, 0x00, 0x1f, 0x8b,
			0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
			0x06, 0x00, 0x42, 0x43, 0x02, 0x00, 0xcf, 0x00,
			0x4d, 0xce, 0x31, 0x6a, 0xc3, 0x30, 0x18, 0x05,
			0x60, 0x41, 0x0f, 0x50, 0xd2, 0x66, 0x30, 0x74,
			0x88, 0xab, 0x18, 0xeb, 0x59, 0xb2, 0xa5, 0xff,
			0xb7, 0xa9, 0x93, 0x8c, 0x32, 0x82, 0x6a, 0x76,
			0xb6, 0xce, 0x3d, 0x40, 0x2e, 0x90, 0x41, 0xc1,
			0x83, 0x73, 0xc1, 0xdc, 0xa4, 0x74, 0xad, 0x4b,
			0xa0, 0xf4, 0x5b, 0xde, 0xe3, 0x4d, 0xef, 0x26,
			0xee, 0xbe, 0xbe, 0xaf, 0x0f, 0xcf, 0xe2, 0x94,
			0x0b, 0x71, 0x16, 0x9f, 0xff, 0xb6, 0xdf, 0x3c,
			0x8e, 0x23, 0xf5, 0x6d, 0xdf, 0xbd, 0x59, 0x3e,
			0xf4, 0xdd, 0x6e, 0xdf, 0xb6, 0x62, 0xb8, 0xc8,
			0xe8, 0x63, 0x98, 0x57, 0x29, 0x44, 0x9f, 0xbc,
			0x9c, 0xa6, 0x4c, 0x66, 0xc5, 0x94, 0x45, 0xa4,
			0x39, 0xad, 0x83, 0x7f, 0x8a, 0x31, 0x0c, 0x4b,
			0x0d, 0x98, 0xb3, 0x20, 0xe1, 0xd7, 0xe9, 0x55,
			0xe6, 0x79, 0xfe, 0x52, 0xa0, 0xd2, 0xa5, 0xde,
			0x42, 0xab, 0x2d, 0x60, 0xea, 0x1a, 0xc6, 0x28,
			0x65, 0xa1, 0x01, 0xab, 0x6c, 0xa9, 0x50, 0x35,
			0x0a, 0x1b, 0xc7, 0xda, 0xb0, 0x61, 0xaa, 0x1d,
			0xa1, 0xd4, 0xa6, 0x65, 0x36, 0x64, 0xa0, 0x1c,
			0x3b, 0x66, 0x22, 0x76, 0x8e, 0xc8, 0x71, 0x45,
			0x0b, 0x0b, 0xba, 0xb3, 0x4d, 0xa3, 0xf1, 0x38,
			0xbe, 0x7f, 0xfc, 0x7d, 0x15, 0x3f, 0xf2, 0x15,
			0x80, 0x8f, 0xda, 0x00, 0x00, 0x00, 0x1f, 0x8b,
			0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
			0x06, 0x00, 0x42, 0x43, 0x02, 0x00, 0x1b, 0x00,
			0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00,
		},
		expect: sam.GroupQuery,
	},
	{
		input: []byte{
			0x1f, 0x8b, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00,
			0x00, 0xff, 0x06, 0x00, 0x42, 0x43, 0x02, 0x00,
			0xf1, 0x01, 0xad, 0x92, 0xcb, 0x8e, 0xda, 0x30,
			0x14, 0x86, 0x33, 0x9d, 0x95, 0x79, 0x8a, 0x2c,
			0x58, 0xb4, 0x0b, 0x1c, 0xe7, 0xe2, 0x40, 0x2c,
			0x55, 0xe2, 0x26, 0x01, 0x52, 0x08, 0x29, 0x11,
			0xad, 0x66, 0x95, 0x3a, 0x89, 0x13, 0xac, 0x31,
			0x0e, 0xe0, 0x00, 0x6d, 0xdf, 0xa0, 0x4f, 0x3c,
			0xbb, 0x51, 0x4d, 0x34, 0x9d, 0x8e, 0xda, 0x2e,
			0x59, 0xf8, 0xc8, 0x3e, 0x97, 0xef, 0x3f, 0xb6,
			0xcf, 0x78, 0xb4, 0xbc, 0xfb, 0x7a, 0x6f, 0x18,
			0xc3, 0xf9, 0x14, 0x7c, 0x8e, 0x88, 0x0d, 0x11,
			0x48, 0x56, 0x24, 0xaf, 0xeb, 0x63, 0xc1, 0x25,
			0x6d, 0x18, 0x98, 0xad, 0xc8, 0x91, 0x95, 0xec,
			0xc8, 0x64, 0xce, 0x3a, 0xc3, 0xe4, 0x13, 0x48,
			0x22, 0xf2, 0x00, 0xc2, 0x88, 0xe0, 0xc0, 0xed,
			0xbb, 0xd8, 0xf7, 0xc1, 0x12, 0x13, 0xbb, 0xa4,
			0xae, 0xd7, 0xf7, 0xfa, 0x18, 0xd1, 0x12, 0x05,
			0xde, 0x20, 0x2b, 0xca, 0xa0, 0x5f, 0x60, 0x8a,
			0x18, 0xc3, 0x0e, 0xc3, 0x36, 0xd8, 0xac, 0x49,
			0xd9, 0xec, 0x89, 0x65, 0x69, 0x0b, 0x6d, 0x84,
			0x50, 0xc5, 0x64, 0xbd, 0x63, 0x0a, 0xb2, 0x8c,
			0x43, 0x9a, 0xc3, 0xd3, 0xa3, 0x75, 0xae, 0x85,
			0x7d, 0x0d, 0x5b, 0x0d, 0xcb, 0xb7, 0x92, 0xe7,
			0x54, 0x58, 0xaf, 0xc2, 0xd6, 0x7e, 0x4b, 0x15,
			0x73, 0xd2, 0x57, 0x47, 0x4a, 0x95, 0x62, 0xbb,
			0x4c, 0x7c, 0x4f, 0x15, 0x3b, 0x9c, 0xda, 0x94,
			0xad, 0x72, 0xb5, 0x24, 0x2c, 0x29, 0xac, 0x7e,
			0x80, 0x51, 0x42, 0xa2, 0xc9, 0x78, 0xe1, 0xf6,
			0x41, 0x12, 0x93, 0xf9, 0x69, 0x47, 0x65, 0x67,
			0xb8, 0x9e, 0x81, 0xc5, 0x94, 0x24, 0xeb, 0x35,
			0xf2, 0x1d, 0xdf, 0xf5, 0x40, 0x38, 0x26, 0xce,
			0xc0, 0xc3, 0x03, 0xec, 0x0f, 0xb0, 0xbe, 0xf5,
			0x92, 0xcc, 0x67, 0xba, 0xb1, 0xc0, 0x07, 0xf1,
			0x82, 0x38, 0xc8, 0x07, 0x93, 0x88, 0x7c, 0xd9,
			0xcc, 0x92, 0x09, 0x88, 0x43, 0xb2, 0x08, 0xc3,
			0xcd, 0x72, 0x11, 0x8d, 0xc0, 0x34, 0xd1, 0x84,
			0x18, 0x21, 0xdb, 0x09, 0xbc, 0xbf, 0x91, 0xf8,
			0xe6, 0x48, 0xcf, 0xbe, 0x05, 0x32, 0x6e, 0x91,
			0xd9, 0x85, 0xa6, 0x5c, 0x16, 0xec, 0x1b, 0x88,
			0xa3, 0xeb, 0xe1, 0xfa, 0xdd, 0x08, 0x62, 0x18,
			0xf4, 0x8e, 0xb6, 0xc6, 0x84, 0x57, 0x9f, 0xd9,
			0x26, 0x98, 0x3d, 0x6a, 0x66, 0x97, 0x46, 0x5d,
			0xcc, 0xee, 0x9f, 0x07, 0x2f, 0xa9, 0x6a, 0xe8,
			0x5b, 0x16, 0x15, 0xb2, 0x75, 0x1e, 0x7e, 0xf3,
			0xe2, 0xf8, 0x8d, 0xc6, 0xff, 0xe0, 0xba, 0xc2,
			0xec, 0x1d, 0x4c, 0x1b, 0x9b, 0xbd, 0xd2, 0xec,
			0x2a, 0xca, 0xd3, 0x92, 0x0b, 0xf6, 0x8f, 0x88,
			0xd9, 0x6d, 0xb1, 0x6d, 0xb0, 0x33, 0x9c, 0xac,
			0x40, 0xf7, 0x51, 0xd6, 0x17, 0xd9, 0x82, 0x85,
			0x6a, 0xdd, 0xef, 0xd5, 0x07, 0xf3, 0xa3, 0x79,
			0x8b, 0x89, 0xda, 0xd1, 0xfd, 0x9e, 0xcb, 0x4a,
			0x4f, 0x96, 0xaa, 0x4f, 0xc7, 0x9c, 0x29, 0x6b,
			0x14, 0x86, 0xf0, 0x52, 0x29, 0xf8, 0x22, 0xb7,
			0xe3, 0x42, 0xdb, 0x82, 0x9d, 0xb9, 0x64, 0xe9,
			0xb6, 0xb2, 0x83, 0x54, 0xb0, 0xb2, 0x19, 0x09,
			0x5e, 0x49, 0x56, 0xa4, 0x79, 0x2d, 0x04, 0xdd,
			0x2b, 0xbd, 0x2b, 0xea, 0x53, 0x26, 0x74, 0x06,
			0x6f, 0x5e, 0x2a, 0xa1, 0xe2, 0x8d, 0x6e, 0xe7,
			0x9c, 0x97, 0x7a, 0x20, 0x3b, 0x77, 0x86, 0x61,
			0xbc, 0xd3, 0xeb, 0xc1, 0x78, 0x7e, 0xfa, 0x79,
			0xff, 0x0b, 0xf6, 0x33, 0x1f, 0x79, 0x76, 0x03,
			0x00, 0x00, 0x1f, 0x8b, 0x08, 0x04, 0x00, 0x00,
			0x00, 0x00, 0x00, 0xff, 0x06, 0x00, 0x42, 0x43,
			0x02, 0x00, 0xcf, 0x00, 0x4d, 0xce, 0x31, 0x6a,
			0xc3, 0x30, 0x18, 0x05, 0x60, 0x41, 0x0f, 0x50,
			0xd2, 0x66, 0x30, 0x74, 0x88, 0xab, 0x18, 0xeb,
			0x59, 0xb2, 0xa5, 0xff, 0xb7, 0xa9, 0x93, 0x8c,
			0x32, 0x82, 0x6a, 0x76, 0xb6, 0xce, 0x3d, 0x40,
			0x2e, 0x90, 0x41, 0xc1, 0x83, 0x73, 0xc1, 0xdc,
			0xa4, 0x74, 0xad, 0x4b, 0xa0, 0xf4, 0x5b, 0xde,
			0xe3, 0x4d, 0xef, 0x26, 0xee, 0xbe, 0xbe, 0xaf,
			0x0f, 0xcf, 0xe2, 0x94, 0x0b, 0x71, 0x16, 0x9f,
			0xff, 0xb6, 0xdf, 0x3c, 0x8e, 0x23, 0xf5, 0x6d,
			0xdf, 0xbd, 0x59, 0x3e, 0xf4, 0xdd, 0x6e, 0xdf,
			0xb6, 0x62, 0xb8, 0xc8, 0xe8, 0x63, 0x98, 0x57,
			0x29, 0x44, 0x9f, 0xbc, 0x9c, 0xa6, 0x4c, 0x66,
			0xc5, 0x94, 0x45, 0xa4, 0x39, 0xad, 0x83, 0x7f,
			0x8a, 0x31, 0x0c, 0x4b, 0x0d, 0x98, 0xb3, 0x20,
			0xe1, 0xd7, 0xe9, 0x55, 0xe6, 0x79, 0xfe, 0x52,
			0xa0, 0xd2, 0xa5, 0xde, 0x42, 0xab, 0x2d, 0x60,
			0xea, 0x1a, 0xc6, 0x28, 0x65, 0xa1, 0x01, 0xab,
			0x6c, 0xa9, 0x50, 0x35, 0x0a, 0x1b, 0xc7, 0xda,
			0xb0, 0x61, 0xaa, 0x1d, 0xa1, 0xd4, 0xa6, 0x65,
			0x36, 0x64, 0xa0, 0x1c, 0x3b, 0x66, 0x22, 0x76,
			0x8e, 0xc8, 0x71, 0x45, 0x0b, 0x0b, 0xba, 0xb3,
			0x4d, 0xa3, 0xf1, 0x38, 0xbe, 0x7f, 0xfc, 0x7d,
			0x15, 0x3f, 0xf2, 0x15, 0x80, 0x8f, 0xda, 0x00,
			0x00, 0x00, 0x1f, 0x8b, 0x08, 0x04, 0x00, 0x00,
			0x00, 0x00, 0x00, 0xff, 0x06, 0x00, 0x42, 0x43,
			0x02, 0x00, 0x1b, 0x00, 0x03, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		},
		expect: sam.GroupReference,
	},
}

func (s *S) TestIssue13(c *check.C) {
	br, err := NewReader(bytes.NewReader(issue13), *conc)
	c.Assert(err, check.Equals, nil, check.Commentf("Failed to open BAM"))

	r, err := br.Read()
	c.Assert(err, check.Equals, nil, check.Commentf("Failed to read BAM record"))

	end := r.End()
	c.Check(end, check.Equals, 4391)
}

// This is BAM data provided in issue 12 [sic] from SAM file in comment 4.
// Issue 12 as described is working as intended.
//
//	@HD	VN:1.0	SO:coordinate
//	@SQ	SN:Zv9_NA129	LN:9079
//	10	0	Zv9_NA129	4272	50	40M75N5M	*	0	0	TGACGCAGAGGTGCGCAAGACCTCCAAGAAGCAGCTGAAGGACAC	DDDDHGHHHJI<ABGGIHGIGGHIJEEEGGGCBFCEHIIFFHECI	AS:i:-3	XN:i:0	XM:i:1	XO:i:0	XG:i:0	NM:i:1	MD:Z:0C44	YT:Z:UU	XS:A:+	NH:i:1
var issue13 = []byte{
	0x1f, 0x8b, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00,
	0x00, 0xff, 0x06, 0x00, 0x42, 0x43, 0x02, 0x00,
	0x61, 0x00, 0x73, 0x72, 0xf4, 0x65, 0x34, 0x62,
	0x60, 0x60, 0x70, 0xf0, 0x70, 0xe1, 0x0c, 0xf3,
	0xb3, 0x32, 0xd4, 0x33, 0xe0, 0x0c, 0xf6, 0xb7,
	0x4a, 0xce, 0xcf, 0x2f, 0x4a, 0xc9, 0xcc, 0x4b,
	0x2c, 0x49, 0xe5, 0x72, 0x08, 0x0e, 0xe4, 0x0c,
	0xf6, 0xb3, 0x8a, 0x2a, 0xb3, 0x8c, 0xf7, 0x73,
	0x34, 0x34, 0xb2, 0xe4, 0xf4, 0xf1, 0xb3, 0xb2,
	0x34, 0x30, 0xb7, 0xe4, 0x62, 0x04, 0xea, 0xe2,
	0x02, 0x62, 0xb8, 0x0c, 0x43, 0xb9, 0x32, 0x03,
	0x03, 0x00, 0x03, 0x99, 0x97, 0x23, 0x50, 0x00,
	0x00, 0x00, 0x1f, 0x8b, 0x08, 0x04, 0x00, 0x00,
	0x00, 0x00, 0x00, 0xff, 0x06, 0x00, 0x42, 0x43,
	0x02, 0x00, 0xab, 0x00, 0x2d, 0x89, 0x41, 0x0a,
	0xc2, 0x30, 0x10, 0x45, 0xa7, 0xd6, 0x03, 0xd8,
	0x92, 0x9d, 0x9b, 0x26, 0xa6, 0x33, 0x53, 0x44,
	0xa8, 0xd2, 0x0b, 0x24, 0x11, 0x6c, 0x17, 0xad,
	0x42, 0x2d, 0xc4, 0x6e, 0xbd, 0x88, 0x88, 0x17,
	0xf1, 0x04, 0x2e, 0x3c, 0x9f, 0x68, 0x44, 0x1f,
	0x7c, 0xfe, 0xfb, 0xfc, 0x3b, 0xfc, 0x78, 0xcc,
	0x00, 0xe2, 0x4d, 0x93, 0xc6, 0xc1, 0x57, 0x21,
	0xef, 0x3f, 0xdf, 0x6f, 0x5d, 0xc2, 0x65, 0x02,
	0xf0, 0x9c, 0x02, 0x1c, 0xc2, 0xbc, 0xa5, 0x56,
	0x88, 0xda, 0xda, 0xc4, 0xa8, 0xab, 0x14, 0x89,
	0x15, 0x6c, 0x84, 0x91, 0xd9, 0x22, 0x40, 0x48,
	0x44, 0x05, 0xcf, 0x33, 0x89, 0xc8, 0x84, 0x8c,
	0x48, 0x5c, 0x68, 0xad, 0x11, 0x51, 0xc9, 0x5c,
	0x69, 0x62, 0xce, 0x73, 0xd2, 0x8a, 0x4d, 0x7f,
	0x7e, 0xf9, 0xce, 0x81, 0x6f, 0x5d, 0xe4, 0xf7,
	0xa1, 0x77, 0x0e, 0xba, 0xe0, 0xed, 0x76, 0x2c,
	0x5d, 0x55, 0xc1, 0xe9, 0x38, 0x0e, 0x03, 0xf8,
	0xde, 0x2c, 0xbb, 0xda, 0x45, 0x1f, 0xf4, 0x97,
	0xb4, 0x05, 0xa5, 0x00, 0x00, 0x00, 0x1f, 0x8b,
	0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
	0x06, 0x00, 0x42, 0x43, 0x02, 0x00, 0x1b, 0x00,
	0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00,
}

func (s *S) TestIssueDate(c *check.C) {
	br, err := NewReader(bytes.NewReader(issueDate), *conc)
	c.Assert(err, check.Equals, nil, check.Commentf("Failed to open BAM"))

	rg := br.Header().RGs()
	c.Assert(len(rg), check.Equals, 1)
	expect := time.Date(2014, 03, 25, 12, 26, 51, 0, time.FixedZone("", -int(7*time.Hour/time.Second)))
	c.Check(rg[0].Time(), check.DeepEquals, expect)
}

// This is BAM data from the following SAM.
//
//	@HD	VN:1.0
//	@SQ	SN:ref	LN:1
//	@RG	ID:rg	DT:2014-03-25T12:26:51-0700
//	r1	125	ref	-1	0	*	=	-1	0	A	B	RG:Z:rg
var issueDate = []byte{
	0x1f, 0x8b, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00,
	0x00, 0xff, 0x06, 0x00, 0x42, 0x43, 0x02, 0x00,
	0x6d, 0x00, 0x73, 0x72, 0xf4, 0x65, 0x74, 0x64,
	0x60, 0x60, 0x70, 0xf0, 0x70, 0xe1, 0x0c, 0xf3,
	0xb3, 0x32, 0xd4, 0x33, 0xe0, 0x72, 0x08, 0x0e,
	0xe4, 0x0c, 0xf6, 0xb3, 0x2a, 0x4a, 0x4d, 0xe3,
	0xf4, 0x01, 0x8a, 0x70, 0x39, 0x04, 0xb9, 0x73,
	0x7a, 0xba, 0x58, 0x15, 0xa5, 0x73, 0xba, 0x84,
	0x58, 0x19, 0x19, 0x18, 0x9a, 0xe8, 0x1a, 0x18,
	0xeb, 0x1a, 0x99, 0x86, 0x18, 0x1a, 0x59, 0x19,
	0x99, 0x59, 0x99, 0x1a, 0xea, 0x1a, 0x98, 0x1b,
	0x18, 0x70, 0x31, 0x02, 0x0d, 0x61, 0x01, 0x62,
	0xa0, 0x36, 0x06, 0x10, 0x1b, 0x00, 0x38, 0x08,
	0xd9, 0xec, 0x59, 0x00, 0x00, 0x00, 0x1f, 0x8b,
	0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
	0x06, 0x00, 0x42, 0x43, 0x02, 0x00, 0x38, 0x00,
	0xd3, 0x66, 0x80, 0x80, 0xff, 0x40, 0xc0, 0xcc,
	0xe0, 0x21, 0xc4, 0xc0, 0x50, 0xcb, 0xc0, 0x88,
	0x24, 0x06, 0xa2, 0x8b, 0x0c, 0x19, 0x04, 0x14,
	0x83, 0xdc, 0xa3, 0x8a, 0xd2, 0x19, 0x00, 0xe6,
	0x18, 0xef, 0x3c, 0x2f, 0x00, 0x00, 0x00, 0x1f,
	0x8b, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
	0xff, 0x06, 0x00, 0x42, 0x43, 0x02, 0x00, 0x1b,
	0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00,
}

var baiTestData = []struct {
	data   []byte
	expect *Index
	err    error
}{
	{
		/*
		   0x42, 0x41, 0x49, 0x01, // BAI\1
		   0x01, 0x00, 0x00, 0x00, // n_ref

		   0x02, 0x00, 0x00, 0x00, // n_bin

		   		0x49, 0x12, 0x00, 0x00, // bin
		   		0x01, 0x00, 0x00, 0x00, // n_chunk

		   			0x00, 0x00, 0x62, 0x00,	0x00, 0x00, 0x00, 0x00, // chunk_beg
		   			0x00, 0x00, 0x91, 0x01,	0x00, 0x00, 0x00, 0x00, // chunk_end

		   		0x4a, 0x92, 0x00, 0x00, // bin - always 0x924a
		   		0x02, 0x00, 0x00, 0x00, // n_chunk - always 2

		   			0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, // unmapped_beg
		   			0x00, 0x00, 0x91, 0x01,	0x00, 0x00, 0x00, 0x00, // unmapped_end

		   			0x08, 0x00, 0x00, 0x00,	0x00, 0x00, 0x00, 0x00, // mapped_count
		   			0x01, 0x00, 0x00, 0x00,	0x00, 0x00, 0x00, 0x00, // unmapped_count

		   0x01, 0x00, 0x00, 0x00, // n_intv

		   		0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, // ioffset

		   0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // n_no_coor
		*/
		data: []byte{
			0x42, 0x41, 0x49, 0x01, 0x01, 0x00, 0x00, 0x00,
			0x02, 0x00, 0x00, 0x00, 0x49, 0x12, 0x00, 0x00,
			0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0x01,
			0x00, 0x00, 0x00, 0x00, 0x4a, 0x92, 0x00, 0x00,
			0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0x01,
			0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		},
		expect: &Index{idx: internal.Index{
			Refs: []internal.RefIndex{
				{
					Bins: []internal.Bin{
						{
							Bin: 4681, Chunks: []bgzf.Chunk{
								{Begin: bgzf.Offset{File: 98, Block: 0}, End: bgzf.Offset{File: 401, Block: 0}},
							},
						},
					},
					Stats: &internal.ReferenceStats{
						Chunk:    bgzf.Chunk{Begin: bgzf.Offset{File: 98, Block: 0}, End: bgzf.Offset{File: 401, Block: 0}},
						Mapped:   8,
						Unmapped: 1,
					},
					Intervals: []bgzf.Offset{{File: 98, Block: 0}},
				},
			},
			Unmapped:   uint64ptr(1),
			IsSorted:   true,
			LastRecord: maxInt,
		}},
		err: nil,
	},
	{
		/*
		   0x42, 0x41, 0x49, 0x01, // BAI\1
		   0x01, 0x00, 0x00, 0x00, // n_ref

		   0x02, 0x00, 0x00, 0x00, // n_bin

		   		0x49, 0x12, 0x00, 0x00, // bin
		   		0x01, 0x00, 0x00, 0x00, // n_chunk

		   			0x00, 0x00, 0x62, 0x00,	0x00, 0x00, 0x00, 0x00, // chunk_beg
		   			0x00, 0x00, 0x91, 0x01,	0x00, 0x00, 0x00, 0x00, // chunk_end

		   		0x4a, 0x92, 0x00, 0x00, // bin - always 0x924a
		   		0x02, 0x00, 0x00, 0x00, // n_chunk - always 2

		   			0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, // unmapped_beg
		   			0x00, 0x00, 0x91, 0x01,	0x00, 0x00, 0x00, 0x00, // unmapped_end

		   			0x08, 0x00, 0x00, 0x00,	0x00, 0x00, 0x00, 0x00, // mapped_count
		   			0x01, 0x00, 0x00, 0x00,	0x00, 0x00, 0x00, 0x00, // unmapped_count

		   0x01, 0x00, 0x00, 0x00, // n_intv

		   		0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, // ioffset
		*/
		data: []byte{
			0x42, 0x41, 0x49, 0x01, 0x01, 0x00, 0x00, 0x00,
			0x02, 0x00, 0x00, 0x00, 0x49, 0x12, 0x00, 0x00,
			0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0x01,
			0x00, 0x00, 0x00, 0x00, 0x4a, 0x92, 0x00, 0x00,
			0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0x01,
			0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00,
		},
		expect: &Index{idx: internal.Index{
			Refs: []internal.RefIndex{
				{
					Bins: []internal.Bin{
						{
							Bin: 4681, Chunks: []bgzf.Chunk{
								{Begin: bgzf.Offset{File: 98, Block: 0}, End: bgzf.Offset{File: 401, Block: 0}},
							},
						},
					},
					Stats: &internal.ReferenceStats{
						Chunk:    bgzf.Chunk{Begin: bgzf.Offset{File: 98, Block: 0}, End: bgzf.Offset{File: 401, Block: 0}},
						Mapped:   8,
						Unmapped: 1,
					},
					Intervals: []bgzf.Offset{{File: 98, Block: 0}},
				},
			},
			Unmapped:   nil,
			IsSorted:   true,
			LastRecord: maxInt,
		}},
		err: nil,
	},
	{
		/*
		   0x42, 0x41, 0x49, 0x01, // BAI\1
		   0x01, 0x00, 0x00, 0x00, // n_ref

		   0x01, 0x00, 0x00, 0x00, // n_bin

		   		0x49, 0x12, 0x00, 0x00, // bin
		   		0x01, 0x00, 0x00, 0x00, // n_chunk

		   			0x00, 0x00, 0x62, 0x00,	0x00, 0x00, 0x00, 0x00, // chunk_beg
		   			0x00, 0x00, 0x91, 0x01,	0x00, 0x00, 0x00, 0x00, // chunk_end

		   0x01, 0x00, 0x00, 0x00, // n_intv

		   		0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, // ioffset

		   0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // n_no_coor
		*/
		data: []byte{
			0x42, 0x41, 0x49, 0x01, 0x01, 0x00, 0x00, 0x00,
			0x01, 0x00, 0x00, 0x00, 0x49, 0x12, 0x00, 0x00,
			0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0x01,
			0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		},
		expect: &Index{idx: internal.Index{
			Refs: []internal.RefIndex{
				{
					Bins: []internal.Bin{
						{
							Bin: 4681, Chunks: []bgzf.Chunk{
								{Begin: bgzf.Offset{File: 98, Block: 0}, End: bgzf.Offset{File: 401, Block: 0}},
							},
						},
					},
					Stats:     nil,
					Intervals: []bgzf.Offset{{File: 98, Block: 0}},
				},
			},
			Unmapped:   uint64ptr(1),
			IsSorted:   true,
			LastRecord: maxInt,
		}},
		err: nil,
	},
	{
		/*
		   0x42, 0x41, 0x49, 0x01, // BAI\1
		   0x01, 0x00, 0x00, 0x00, // n_ref

		   0x01, 0x00, 0x00, 0x00, // n_bin

		   		0x49, 0x12, 0x00, 0x00, // bin
		   		0x01, 0x00, 0x00, 0x00, // n_chunk

		   			0x00, 0x00, 0x62, 0x00,	0x00, 0x00, 0x00, 0x00, // chunk_beg
		   			0x00, 0x00, 0x91, 0x01,	0x00, 0x00, 0x00, 0x00, // chunk_end

		   0x01, 0x00, 0x00, 0x00, // n_intv

		   		0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, // ioffset
		*/
		data: []byte{
			0x42, 0x41, 0x49, 0x01, 0x01, 0x00, 0x00, 0x00,
			0x01, 0x00, 0x00, 0x00, 0x49, 0x12, 0x00, 0x00,
			0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0x01,
			0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00,
		},
		expect: &Index{idx: internal.Index{
			Refs: []internal.RefIndex{
				{
					Bins: []internal.Bin{
						{
							Bin: 4681, Chunks: []bgzf.Chunk{
								{Begin: bgzf.Offset{File: 98, Block: 0}, End: bgzf.Offset{File: 401, Block: 0}},
							},
						},
					},
					Stats:     nil,
					Intervals: []bgzf.Offset{{File: 98, Block: 0}},
				},
			},
			Unmapped:   nil,
			IsSorted:   true,
			LastRecord: maxInt,
		}},
		err: nil,
	},
	{
		/*
			   0x42, 0x41, 0x49, 0x01, // BAI\1
			   0x56, 0x00, 0x00, 0x00, // n_ref

			   	0x00, 0x00, 0x00, 0x00, // empty bins
				[ lines removed ]
			   	0x00, 0x00, 0x00, 0x00, // empty intervals

			   	0x02, 0x00, 0x00, 0x00, // n_bins - ref 23
			   		0x4a, 0x92, 0x00, 0x00, // bin - always 0x924a
			   		0x02, 0x00, 0x00, 0x00, // n_chunk - always 2
			   			0x00, 0x00, 0x46, 0x12, 0x00, 0x00, 0x00, 0x00, // unmapped_beg
			   			0xf9, 0x1c, 0x46, 0x12, 0x00, 0x00, 0x00, 0x00, // unmapped_end
			   			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mapped_count
			   			0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // unmapped_count

			   		0x70, 0x20, 0x00, 0x00, // bin
			   		0x01, 0x00, 0x00, 0x00, // n_chunk
			   			0x00, 0x00, 0x46, 0x12, 0x00, 0x00, 0x00, 0x00, // chunk_beg
			   			0xf9, 0x1c, 0x46, 0x12, 0x00, 0x00, 0x00, 0x00, // chunk_end

			   	0x00, 0x00, 0x00, 0x00, // n_intv - ref 23

			   	0x02, 0x00, 0x00, 0x00,  // n_bins - ref 24
			   		0x4a, 0x92, 0x00, 0x00, // bin - always 0x924a
			   		0x02, 0x00, 0x00, 0x00, // n_chunk - always 2
			   			0xf9, 0x1c, 0x46, 0x12, 0x00, 0x00, 0x00, 0x00, // unmapped_beg
			   			0x1d, 0x40, 0x46, 0x12, 0x00, 0x00, 0x00, 0x00, // unmapped_end
			   			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mapped_count
			   			0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // unmapped_count

			   		0x4a, 0x12, 0x00, 0x00, // bin
			   		0x01, 0x00, 0x00, 0x00, // n_chunk
			   			0xf9, 0x1c, 0x46, 0x12, 0x00, 0x00, 0x00, 0x00, // chunk_beg
			   			0x1d, 0x40, 0x46, 0x12, 0x00, 0x00, 0x00, 0x00, // chunk_end

			   	0x00, 0x00, 0x00, 0x00, // n_intv - ref 24

			   	0x00, 0x00, 0x00, 0x00, // empty bins
				[ lines removed ]
			   	0x00, 0x00, 0x00, 0x00, // empty intervals

			   	0x02, 0x00, 0x00, 0x00,  // n_bins - ref 72
			   		0x53, 0x12, 0x00, 0x00, // bin
			   		0x01, 0x00, 0x00, 0x00, // n_chunk
			   			0x1d, 0x40, 0x46, 0x12, 0x00, 0x00, 0x00, 0x00, // chunk_beg
			   			0xf5, 0x41, 0x46, 0x12, 0x00, 0x00, 0x00, 0x00, // chunk_end

			   		0x4a, 0x92, 0x00, 0x00, // bin - always 0x924a
			   		0x02, 0x00, 0x00, 0x00, // n_chunk - always 2
			   			0x1d, 0x40, 0x46, 0x12, 0x00, 0x00, 0x00, 0x00, // unmapped_beg
			   			0xf5, 0x41, 0x46, 0x12, 0x00, 0x00, 0x00, 0x00, // unmapped_end
			   			0x00, 0x00, 0x00, 0x00,	0x00, 0x00, 0x00, 0x00, // mapped_count
			   			0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // unmapped_count

			   	0x00, 0x00, 0x00, 0x00, // n_intv - ref 72

			   	0x00, 0x00, 0x00, 0x00, // empty bins
				[ lines removed ]
			   	0x00, 0x00, 0x00, 0x00, // empty intervals

			   0xa4, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // n_no_coor
		*/
		data: []byte{
			0x42, 0x41, 0x49, 0x01, 0x56, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x02, 0x00, 0x00, 0x00, 0x4a, 0x92, 0x00, 0x00,
			0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x12,
			0x00, 0x00, 0x00, 0x00, 0xf9, 0x1c, 0x46, 0x12,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x70, 0x20, 0x00, 0x00,
			0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x12,
			0x00, 0x00, 0x00, 0x00, 0xf9, 0x1c, 0x46, 0x12,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x02, 0x00, 0x00, 0x00, 0x4a, 0x92, 0x00, 0x00,
			0x02, 0x00, 0x00, 0x00, 0xf9, 0x1c, 0x46, 0x12,
			0x00, 0x00, 0x00, 0x00, 0x1d, 0x40, 0x46, 0x12,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x4a, 0x12, 0x00, 0x00,
			0x01, 0x00, 0x00, 0x00, 0xf9, 0x1c, 0x46, 0x12,
			0x00, 0x00, 0x00, 0x00, 0x1d, 0x40, 0x46, 0x12,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x02, 0x00, 0x00, 0x00, 0x53, 0x12, 0x00, 0x00,
			0x01, 0x00, 0x00, 0x00, 0x1d, 0x40, 0x46, 0x12,
			0x00, 0x00, 0x00, 0x00, 0xf5, 0x41, 0x46, 0x12,
			0x00, 0x00, 0x00, 0x00, 0x4a, 0x92, 0x00, 0x00,
			0x02, 0x00, 0x00, 0x00, 0x1d, 0x40, 0x46, 0x12,
			0x00, 0x00, 0x00, 0x00, 0xf5, 0x41, 0x46, 0x12,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0xa4, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		},
		expect: &Index{idx: internal.Index{
			Refs: func() []internal.RefIndex {
				idx := [86]internal.RefIndex{
					23: {
						Bins: []internal.Bin{
							{
								Bin: 0x2070,
								Chunks: []bgzf.Chunk{
									{
										Begin: bgzf.Offset{File: 0x1246, Block: 0x0},
										End:   bgzf.Offset{File: 0x1246, Block: 0x1cf9},
									},
								},
							},
						},
						Stats: &internal.ReferenceStats{
							Chunk: bgzf.Chunk{
								Begin: bgzf.Offset{File: 0x1246, Block: 0x0},
								End:   bgzf.Offset{File: 0x1246, Block: 0x1cf9},
							},
							Mapped: 0, Unmapped: 0x1d,
						},
						Intervals: nil,
					},
					24: {
						Bins: []internal.Bin{
							{
								Bin: 0x124a,
								Chunks: []bgzf.Chunk{
									{
										Begin: bgzf.Offset{File: 0x1246, Block: 0x1cf9},
										End:   bgzf.Offset{File: 0x1246, Block: 0x401d},
									},
								},
							},
						},
						Stats: &internal.ReferenceStats{
							Chunk: bgzf.Chunk{
								Begin: bgzf.Offset{File: 0x1246, Block: 0x1cf9},
								End:   bgzf.Offset{File: 0x1246, Block: 0x401d},
							},
							Mapped: 0, Unmapped: 0x25,
						},
						Intervals: nil,
					},
					72: {
						Bins: []internal.Bin{
							{
								Bin: 0x1253,
								Chunks: []bgzf.Chunk{
									{
										Begin: bgzf.Offset{File: 0x1246, Block: 0x401d},
										End:   bgzf.Offset{File: 0x1246, Block: 0x41f5},
									},
								},
							},
						},
						Stats: &internal.ReferenceStats{
							Chunk: bgzf.Chunk{
								Begin: bgzf.Offset{File: 0x1246, Block: 0x401d},
								End:   bgzf.Offset{File: 0x1246, Block: 0x41f5},
							},
							Mapped: 0, Unmapped: 0x2,
						},
						Intervals: nil,
					},
				}
				return idx[:]
			}(),
			Unmapped:   uint64ptr(932),
			IsSorted:   true,
			LastRecord: maxInt,
		}},
		err: nil,
	},
}

func uint64ptr(i uint64) *uint64 {
	return &i
}

func (s *S) TestReadBAI(c *check.C) {
	for _, test := range baiTestData {
		bai, err := ReadIndex(bytes.NewReader(test.data))
		c.Assert(err, check.Equals, test.err)
		c.Check(bai, check.DeepEquals, test.expect)
	}
}

// conceptualBAIdata is gzip compressed BAI for the alignments in the BAM
// corresponding to:
//
// @HD	VN:1.0	SO:coordinate
// @SQ	SN:conceptual	LN:134217728
// 60m66m:bin0	0	conceptual	62914561	40	6291456M	*	0	0	*	*
// 70m76m:bin2	0	conceptual	73400321	40	6291456M	*	0	0	*	*
// 73m75m:bin18	0	conceptual	76546049	40	2097152M	*	0	0	*	*
//
// This is a coordinate-translated version of the conceptual example in the
// SAM spec using binning as actually used by BAM rather than as presented.
var conceptualBAIdata = []byte{
	0x1f, 0x8b, 0x08, 0x08, 0xe3, 0xf6, 0x2c, 0x54,
	0x00, 0x03, 0x63, 0x6f, 0x6e, 0x63, 0x65, 0x70,
	0x74, 0x75, 0x61, 0x6c, 0x2e, 0x62, 0x61, 0x6d,
	0x2e, 0x62, 0x61, 0x69, 0x00, 0xed, 0xcb, 0x41,
	0x0d, 0x85, 0x30, 0x10, 0x40, 0xc1, 0x2d, 0xa0,
	0x00, 0x2d, 0x18, 0x80, 0x1b, 0xf8, 0x20, 0x41,
	0x0f, 0x8a, 0x10, 0xf3, 0x85, 0xfc, 0xa4, 0xb4,
	0x07, 0x08, 0x0a, 0x60, 0xe6, 0xb0, 0x2f, 0x6d,
	0xb7, 0xd3, 0x38, 0xa7, 0x14, 0x11, 0x5d, 0x9c,
	0x52, 0x9e, 0x6b, 0x9e, 0x43, 0x69, 0x53, 0xee,
	0xeb, 0x79, 0x2b, 0x5d, 0xf6, 0xf3, 0xad, 0xee,
	0x47, 0xfc, 0xf2, 0x6c, 0xe3, 0xaa, 0x2f, 0xff,
	0xb7, 0xdb, 0xde, 0xd1, 0x07, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0xf0, 0x68, 0x55, 0x55, 0x55, 0x55,
	0x55, 0x55, 0x55, 0x55, 0x55, 0xd5, 0x97, 0x76,
	0x50, 0x55, 0x55, 0x55, 0xd5, 0x4f, 0xb5, 0xfa,
	0x03, 0x6e, 0x81, 0xb2, 0xab, 0x88, 0x96, 0x00,
	0x00,
}

var conceptualBAMdata = []byte{
	// sam.Header block [{File:0, Block:0}, {File:0, Block:87}).
	0x1f, 0x8b, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
	0x06, 0x00, 0x42, 0x43, 0x02, 0x00, 0x64, 0x00, 0x73, 0x72,
	0xf4, 0x65, 0xb4, 0x60, 0x60, 0x60, 0x70, 0xf0, 0x70, 0xe1,
	0x0c, 0xf3, 0xb3, 0x32, 0xd4, 0x33, 0xe0, 0x0c, 0xf6, 0xb7,
	0x4a, 0xce, 0xcf, 0x2f, 0x4a, 0xc9, 0xcc, 0x4b, 0x2c, 0x49,
	0xe5, 0x72, 0x08, 0x0e, 0xe4, 0x0c, 0xf6, 0x03, 0x8a, 0xe4,
	0x25, 0xa7, 0x16, 0x94, 0x94, 0x26, 0xe6, 0x70, 0xfa, 0x00,
	0x95, 0x19, 0x9b, 0x18, 0x19, 0x9a, 0x9b, 0x1b, 0x59, 0x70,
	0x31, 0x02, 0xf5, 0x72, 0x03, 0x31, 0x42, 0x1e, 0xc8, 0x61,
	0xe0, 0x00, 0x00, 0x42, 0x51, 0xcc, 0xea, 0x57, 0x00, 0x00,
	0x00,

	// Record block [{File:101, Block:0}, {File:101, Block:157}).
	0x1f, 0x8b, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
	0x06, 0x00, 0x42, 0x43, 0x02, 0x00, 0x62, 0x00, 0x33, 0x60,
	0x80, 0x81, 0x03, 0xcc, 0x3c, 0x1a, 0x0c, 0x0c, 0x8c, 0x50,
	0xde, 0x7f, 0x28, 0x00, 0xb1, 0xcd, 0x0c, 0x72, 0xcd, 0xcc,
	0x72, 0xad, 0x92, 0x32, 0xf3, 0x0c, 0x40, 0x5c, 0x36, 0x03,
	0xb8, 0x9e, 0x04, 0x16, 0x1e, 0x0d, 0x26, 0xac, 0x7a, 0xcc,
	0x0d, 0x72, 0xcd, 0x21, 0x7a, 0x8c, 0xc0, 0x7a, 0x0c, 0xe1,
	0x7a, 0x26, 0xb0, 0xf0, 0x6a, 0x08, 0x61, 0xd7, 0x63, 0x9c,
	0x6b, 0x6e, 0x0a, 0xd6, 0x63, 0x68, 0x01, 0xe2, 0x33, 0x01,
	0x00, 0x5a, 0x80, 0xfe, 0xec, 0x9d, 0x00, 0x00, 0x00,

	// Magic block [{File:200, Block:0}, {File:200, Block:0}).
	0x1f, 0x8b, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
	0x06, 0x00, 0x42, 0x43, 0x02, 0x00, 0x1b, 0x00, 0x03, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

	// End {File:228, Block:0}
}

// conceptualChunks differs from the chunks described in chunkTests
// since samtools defines the end offset of the last chunk as the
// end of the file. We define it as the end of the chunk itself.
var conceptualChunks = []bgzf.Chunk{
	{Begin: bgzf.Offset{File: 0, Block: 0}, End: bgzf.Offset{File: 0, Block: 87}},        // header
	{Begin: bgzf.Offset{File: 101, Block: 0}, End: bgzf.Offset{File: 101, Block: 52}},    // 60m66m:bin0
	{Begin: bgzf.Offset{File: 101, Block: 52}, End: bgzf.Offset{File: 101, Block: 104}},  // 70m76m:bin2
	{Begin: bgzf.Offset{File: 101, Block: 104}, End: bgzf.Offset{File: 101, Block: 157}}, // 73m75m:bin18
	{Begin: bgzf.Offset{File: 101, Block: 153}, End: bgzf.Offset{File: 101, Block: 157}}, // EOF - not checked.
}

func (s *S) TestConceptualBAM(c *check.C) {
	br, err := NewReader(bytes.NewReader(conceptualBAMdata), *conc)
	c.Assert(err, check.Equals, nil)
	c.Check(br.LastChunk(), check.Equals, conceptualChunks[0])
	for _, chunk := range conceptualChunks[1:] {
		_, err := br.Read()
		if err != nil {
			break
		}
		c.Check(br.LastChunk(), check.Equals, chunk)
	}
}

var chunkTests = []struct {
	beg, end int
	expect   []bgzf.Chunk
	err      error
}{
	{
		beg: 65000, end: 71000, // Not in covered region.
		expect: []bgzf.Chunk{
			{Begin: bgzf.Offset{File: 101, Block: 0}, End: bgzf.Offset{File: 101, Block: 52}},
		},
		err: nil,
	},
	{
		beg: 77594624, end: 80740352, // 73m77m:bin2+bin18 - This is the equivalent to the given example.
		expect: []bgzf.Chunk{
			{Begin: bgzf.Offset{File: 101, Block: 52}, End: bgzf.Offset{File: 228, Block: 0}},
		},
		err: nil,
	},
	{
		beg: 62914561, end: 68157440, // 60m65m:bin0+bin2
		expect: []bgzf.Chunk{
			{Begin: bgzf.Offset{File: 101, Block: 0}, End: bgzf.Offset{File: 101, Block: 104}},
		},
		err: nil,
	},
	{
		beg: 72351744, end: 80740352, // 69m77m:bin0+bin2+18
		expect: []bgzf.Chunk{
			{Begin: bgzf.Offset{File: 101, Block: 0}, End: bgzf.Offset{File: 228, Block: 0}},
		},
		err: nil,
	},
	{
		beg: 61865984, end: 80740352, // 59m77m:bin0+bin2+bin18
		expect: []bgzf.Chunk{
			{Begin: bgzf.Offset{File: 101, Block: 0}, End: bgzf.Offset{File: 228, Block: 0}},
		},
		err: nil,
	},
	{
		beg: 80740352, end: 81788928, // 77m78m - Not in covered region.
		expect: nil,
		err:    index.ErrInvalid,
	},
}

func (s *S) TestConceptualBAI(c *check.C) {
	gz, err := gzip.NewReader(bytes.NewReader(conceptualBAIdata))
	c.Assert(err, check.Equals, nil)
	bai, err := ReadIndex(gz)
	c.Assert(err, check.Equals, nil)

	var ref sam.Reference
	var h sam.Header
	h.AddReference(&ref)

	for _, test := range chunkTests {
		chunks, err := bai.Chunks(&ref, test.beg, test.end)
		c.Check(err, check.Equals, test.err,
			check.Commentf("Unexpected error for [%d,%d).", test.beg, test.end),
		)
		c.Check(chunks, check.DeepEquals, test.expect,
			check.Commentf("Unexpected result for [%d,%d).", test.beg, test.end),
		)
	}
}

// @HD	VN:1.0	SO:coordinate
// @SQ	SN:conceptual	LN:134217728
// 60m66m:bin0	0	conceptual	62914561	40	6291456M	*	0	0	*	*
// 70m76m:bin2	0	conceptual	73400321	40	6291456M	*	0	0	*	*
// 73m75m:bin18	0	conceptual	76546049	40	2097152M	*	0	0	*	*
var (
	conceptual = func() *sam.Reference {
		ref, err := sam.NewReference("conceptual", "0", "unicorns", 134217728, nil, nil)
		if err != nil {
			panic("Failed to initialise conceptual reference")
		}
		// Ensure the reference id is valid.
		sam.NewHeader(nil, []*sam.Reference{ref})
		return ref
	}()
	bamFile = []struct {
		rec   *sam.Record
		chunk bgzf.Chunk
	}{
		{
			rec: &sam.Record{
				Name: "60m66m:bin0", // [62914560,69206016)
				Ref:  conceptual,
				Pos:  62914560,
				MapQ: 40,
				Cigar: sam.Cigar{
					sam.NewCigarOp(sam.CigarMatch, 6291456),
				},
			},
			chunk: bgzf.Chunk{
				Begin: bgzf.Offset{File: 101, Block: 0},
				End:   bgzf.Offset{File: 101, Block: 52},
			},
		},
		{
			rec: &sam.Record{
				Name: "70m76m:bin2", // [73400320,79691776)
				Ref:  conceptual,
				Pos:  73400320,
				MapQ: 40,
				Cigar: sam.Cigar{
					sam.NewCigarOp(sam.CigarMatch, 6291456),
				},
			},
			chunk: bgzf.Chunk{
				Begin: bgzf.Offset{File: 101, Block: 52},
				End:   bgzf.Offset{File: 101, Block: 104},
			},
		},
		{
			rec: &sam.Record{
				Name: "73m75m:bin18", // [76546048,78643200)
				Ref:  conceptual,
				Pos:  76546048,
				MapQ: 40,
				Cigar: sam.Cigar{
					sam.NewCigarOp(sam.CigarMatch, 2097152),
				},
			},
			chunk: bgzf.Chunk{
				Begin: bgzf.Offset{File: 101, Block: 104},
				End:   bgzf.Offset{File: 228, Block: 0},
			},
		},
	}
)

func (s *S) TestAdd(c *check.C) {
	var bai Index
	for _, r := range bamFile {
		c.Assert(bai.Add(r.rec, r.chunk), check.Equals, nil)
	}
	gz, err := gzip.NewReader(bytes.NewReader(conceptualBAIdata))
	c.Assert(err, check.Equals, nil)
	expect, err := ReadIndex(gz)
	c.Check(bai.idx.Refs[0].Bins, check.DeepEquals, expect.idx.Refs[0].Bins)
	c.Check(bai.idx.Refs[0].Stats, check.DeepEquals, expect.idx.Refs[0].Stats)
	c.Check(bai.idx.Unmapped, check.DeepEquals, expect.idx.Unmapped)

	// We check naively for overlap with tiles since we do not merge chunks in Add.
	for i := range bai.idx.Refs[0].Intervals {
		got := bai.idx.Refs[0].Intervals[i]
		tbeg := i * internal.TileWidth
		tend := tbeg + internal.TileWidth
		hasOverlap := false
		for _, r := range bamFile {
			if tbeg < r.rec.End() && tend > r.rec.Start() {
				c.Check(got, check.Equals, r.chunk.Begin,
					check.Commentf("Unexpected tile offset for [%d,%d) got:%+v expect:%+v", tbeg, tend, got, r.chunk.Begin),
				)
				hasOverlap = true
				break
			}
		}
		if !hasOverlap {
			c.Check(got, check.Equals, bgzf.Offset{},
				check.Commentf("Unexpected non-zero offset for [%d,%d) got:%+v", tbeg, tend, got),
			)
		}
	}
}

var chunkMergeTests = []struct {
	index func() *Index

	expectAdjacent *Index

	expectSquash *Index

	compStrat      index.MergeStrategy
	expectCompress *Index
}{
	{
		index: func() *Index {
			return &Index{idx: internal.Index{
				Refs: []internal.RefIndex{
					{
						Bins: []internal.Bin{
							{
								Bin: 0,
								Chunks: []bgzf.Chunk{
									{
										Begin: bgzf.Offset{File: 1, Block: 0},
										End:   bgzf.Offset{File: 2, Block: 0},
									},
									{
										Begin: bgzf.Offset{File: 2, Block: 0},
										End:   bgzf.Offset{File: 3, Block: 0},
									},
								},
							},
							{
								Bin: 1,
								Chunks: []bgzf.Chunk{
									{
										Begin: bgzf.Offset{File: 1, Block: 0},
										End:   bgzf.Offset{File: 2, Block: 0},
									},
									{
										Begin: bgzf.Offset{File: 2, Block: 0},
										End:   bgzf.Offset{File: 3, Block: 0},
									},
								},
							},
						},
					},
				},
			}}
		},
		expectAdjacent: &Index{idx: internal.Index{
			Refs: []internal.RefIndex{
				{
					Bins: []internal.Bin{
						{
							Bin: 0,
							Chunks: []bgzf.Chunk{
								{
									Begin: bgzf.Offset{File: 1, Block: 0},
									End:   bgzf.Offset{File: 3, Block: 0},
								},
							},
						},
						{
							Bin: 1,
							Chunks: []bgzf.Chunk{
								{
									Begin: bgzf.Offset{File: 1, Block: 0},
									End:   bgzf.Offset{File: 3, Block: 0},
								},
							},
						},
					},
				},
			},
		}},
		expectSquash: &Index{idx: internal.Index{
			Refs: []internal.RefIndex{
				{
					Bins: []internal.Bin{
						{
							Bin: 0,
							Chunks: []bgzf.Chunk{
								{
									Begin: bgzf.Offset{File: 1, Block: 0},
									End:   bgzf.Offset{File: 3, Block: 0},
								},
							},
						},
						{
							Bin: 1,
							Chunks: []bgzf.Chunk{
								{
									Begin: bgzf.Offset{File: 1, Block: 0},
									End:   bgzf.Offset{File: 3, Block: 0},
								},
							},
						},
					},
				},
			},
		}},
	},
	{
		index: func() *Index {
			return &Index{idx: internal.Index{
				Refs: []internal.RefIndex{
					{
						Bins: []internal.Bin{
							{
								Bin: 0,
								Chunks: []bgzf.Chunk{
									{
										Begin: bgzf.Offset{File: 1, Block: 0},
										End:   bgzf.Offset{File: 2, Block: 0},
									},
									{
										Begin: bgzf.Offset{File: 3, Block: 0},
										End:   bgzf.Offset{File: 4, Block: 0},
									},
								},
							},
							{
								Bin: 1,
								Chunks: []bgzf.Chunk{
									{
										Begin: bgzf.Offset{File: 1, Block: 0},
										End:   bgzf.Offset{File: 2, Block: 0},
									},
									{
										Begin: bgzf.Offset{File: 3, Block: 0},
										End:   bgzf.Offset{File: 4, Block: 0},
									},
								},
							},
						},
					},
				},
			}}
		},
		expectAdjacent: &Index{idx: internal.Index{
			Refs: []internal.RefIndex{
				{
					Bins: []internal.Bin{
						{
							Bin: 0,
							Chunks: []bgzf.Chunk{
								{
									Begin: bgzf.Offset{File: 1, Block: 0},
									End:   bgzf.Offset{File: 2, Block: 0},
								},
								{
									Begin: bgzf.Offset{File: 3, Block: 0},
									End:   bgzf.Offset{File: 4, Block: 0},
								},
							},
						},
						{
							Bin: 1,
							Chunks: []bgzf.Chunk{
								{
									Begin: bgzf.Offset{File: 1, Block: 0},
									End:   bgzf.Offset{File: 2, Block: 0},
								},
								{
									Begin: bgzf.Offset{File: 3, Block: 0},
									End:   bgzf.Offset{File: 4, Block: 0},
								},
							},
						},
					},
				},
			},
		}},
		expectSquash: &Index{idx: internal.Index{
			Refs: []internal.RefIndex{
				{
					Bins: []internal.Bin{
						{
							Bin: 0,
							Chunks: []bgzf.Chunk{
								{
									Begin: bgzf.Offset{File: 1, Block: 0},
									End:   bgzf.Offset{File: 4, Block: 0},
								},
							},
						},
						{
							Bin: 1,
							Chunks: []bgzf.Chunk{
								{
									Begin: bgzf.Offset{File: 1, Block: 0},
									End:   bgzf.Offset{File: 4, Block: 0},
								},
							},
						},
					},
				},
			},
		}},
	},
	{
		index: func() *Index {
			return &Index{idx: internal.Index{
				Refs: []internal.RefIndex{
					{
						Bins: []internal.Bin{
							{
								Bin: 0,
								Chunks: []bgzf.Chunk{
									{
										Begin: bgzf.Offset{File: 1, Block: 0},
										End:   bgzf.Offset{File: 2, Block: 0},
									},
									{
										Begin: bgzf.Offset{File: 0xffff, Block: 0},
										End:   bgzf.Offset{File: 0x10000, Block: 0},
									},
								},
							},
							{
								Bin: 1,
								Chunks: []bgzf.Chunk{
									{
										Begin: bgzf.Offset{File: 1, Block: 0},
										End:   bgzf.Offset{File: 2, Block: 0},
									},
									{
										Begin: bgzf.Offset{File: 0x4ffff, Block: 0},
										End:   bgzf.Offset{File: 0x50000, Block: 0},
									},
								},
							},
						},
					},
				},
			}}
		},
		compStrat: index.CompressorStrategy(0x20000),
		expectCompress: &Index{idx: internal.Index{
			Refs: []internal.RefIndex{
				{
					Bins: []internal.Bin{
						{
							Bin: 0,
							Chunks: []bgzf.Chunk{
								{
									Begin: bgzf.Offset{File: 1, Block: 0},
									End:   bgzf.Offset{File: 0x10000, Block: 0},
								},
							},
						},
						{
							Bin: 1,
							Chunks: []bgzf.Chunk{
								{
									Begin: bgzf.Offset{File: 1, Block: 0},
									End:   bgzf.Offset{File: 2, Block: 0},
								},
								{
									Begin: bgzf.Offset{File: 0x4ffff, Block: 0},
									End:   bgzf.Offset{File: 0x50000, Block: 0},
								},
							},
						},
					},
				},
			},
		}},
	},
}

func (s *S) TestMergeChunks(c *check.C) {
	var bai *Index
	for _, test := range chunkMergeTests {
		if test.expectAdjacent != nil {
			bai = test.index()
			bai.MergeChunks(index.Adjacent)
			c.Check(bai, check.DeepEquals, test.expectAdjacent)
		}

		if test.expectSquash != nil {
			bai = test.index()
			bai.MergeChunks(index.Squash)
			c.Check(bai, check.DeepEquals, test.expectSquash)
		}

		if test.expectCompress != nil {
			bai = test.index()
			bai.MergeChunks(test.compStrat)
			c.Check(bai, check.DeepEquals, test.expectCompress)
		}
	}
}

func (s *S) TestIndexRoundtrip(c *check.C) {
	for i, test := range baiTestData {
		expect := test.expect
		var buf bytes.Buffer
		c.Assert(WriteIndex(&buf, expect), check.Equals, nil)
		got, err := ReadIndex(&buf)
		c.Assert(err, check.Equals, nil, check.Commentf("Test %d", i))
		c.Check(got, check.DeepEquals, expect, check.Commentf("Test %d", i))
	}
}

func (s *S) TestBufferSharing(c *check.C) {
	// @HD	VN:1.5	GO:none	SO:coordinate
	// @SQ	SN:1	LN:249250621
	// A	1187	1	1000025	60	74M	=	1000064	113	AGCTGGGGGTCCACTGGGCTCAGGGAAGACCCCCTGCCAGGGAGACCCCAGGCGCCTGAATGGCCACGGGAAGG	BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
	// A	1107	1	1000064	60	74M	=	1000064	-113	AGCTGGGGGTCCACTGGGCTCAGGGAAGACCCCCTGCCAGGGAGACCCCAGGCGCCTGAATGGCCACGGGAAGG	DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
	data := []byte{
		0x1f, 0x8b, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x06, 0x00, 0x42, 0x43, 0x02, 0x00,
		0x64, 0x00, 0x73, 0x72, 0xf4, 0x65, 0x34, 0x67, 0x60, 0x60, 0x70, 0xf0, 0x70, 0xe1, 0x0c, 0xf3,
		0xb3, 0x32, 0xd4, 0x33, 0xe5, 0x74, 0xf7, 0xb7, 0xca, 0xcb, 0xcf, 0x4b, 0xe5, 0x0c, 0xf6, 0xb7,
		0x4a, 0xce, 0xcf, 0x2f, 0x4a, 0xc9, 0xcc, 0x4b, 0x2c, 0x49, 0xe5, 0x72, 0x08, 0x0e, 0xe4, 0x0c,
		0x06, 0x2a, 0xe0, 0xf4, 0xf1, 0xb3, 0x32, 0x32, 0xb1, 0x34, 0x32, 0x35, 0x30, 0x33, 0x32, 0xe4,
		0x62, 0x04, 0x6a, 0x65, 0x02, 0x62, 0x43, 0x06, 0x5b, 0xe7, 0xdb, 0x7c, 0x00, 0x65, 0x5c, 0xb8,
		0xd2, 0x4d, 0x00, 0x00, 0x00, 0x1f, 0x8b, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x06,
		0x00, 0x42, 0x43, 0x02, 0x00, 0x73, 0x00, 0x9b, 0xca, 0x00, 0x01, 0x11, 0x4e, 0xfc, 0x0c, 0x4c,
		0x36, 0x6d, 0x42, 0x8c, 0x0c, 0x8b, 0x59, 0xbc, 0xa0, 0x62, 0xf5, 0x40, 0xb1, 0x42, 0x20, 0xed,
		0xc8, 0xb0, 0x80, 0x85, 0x81, 0x41, 0x44, 0xc3, 0xc5, 0xc5, 0x43, 0x49, 0xa8, 0xc5, 0x45, 0x43,
		0xd1, 0xc5, 0x51, 0x44, 0x48, 0x49, 0xa9, 0x45, 0x49, 0xc4, 0x05, 0x48, 0x2b, 0xba, 0xa8, 0x28,
		0xb5, 0x08, 0xb6, 0x38, 0x29, 0xaa, 0xb8, 0x08, 0xba, 0x28, 0x52, 0x0d, 0x4c, 0x45, 0x72, 0x04,
		0xc4, 0x61, 0xc1, 0x28, 0x0e, 0xeb, 0xff, 0xff, 0xff, 0x3f, 0x09, 0x0e, 0x53, 0xa6, 0x1a, 0x00,
		0x00, 0x16, 0x09, 0x97, 0x4a, 0x32, 0x01, 0x00, 0x00, 0x1f, 0x8b, 0x08, 0x04, 0x00, 0x00, 0x00,
		0x00, 0x00, 0xff, 0x06, 0x00, 0x42, 0x43, 0x02, 0x00, 0x1b, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00,
	}
	r, err := NewReader(bytes.NewReader(data), 1)
	c.Assert(err, check.Equals, nil)
	defer r.Close()
	rec, err := r.Read()
	c.Assert(err, check.Equals, nil)
	recClone := *rec
	recClone.Seq.Seq = append(rec.Seq.Seq[:0:0], rec.Seq.Seq...)
	recClone.Qual = append(rec.Qual[:0:0], rec.Qual...)
	recClone.AuxFields = append(rec.AuxFields[:0:0], rec.AuxFields...)
	_, err = r.Read()
	c.Assert(err, check.Equals, nil)
	if !reflect.DeepEqual(rec, &recClone) {
		c.Errorf("unexpected record mutation:\ngot: %v\nwant:%v",
			rec, &recClone)
	}
}

var fuzzCrashers = []string{
	// lText
	"BAM\x01000\x86",
	"BAM\x01\x00\x00\x00v",
	"BAM\x01000\xef",

	// nRef
	"BAM\x01\x00\x00\x00\x00000\xbd",
	"BAM\x01\x00\x00\x00\x000000",
	"BAM\x01\x00\x00\x00\x000000\x00\x00\x00\x000",
	"BAM\x01\x00\x00\x00\x00000\xbf",

	// lName
	"BAM\x01\x00\x00\x00\x000000\x00\x00\x00\x000",
	"BAM\x01\x00\x00\x00\x00000\xbf",

	// reader.go 412
	"BAM\x01\xbf\xbdC_t\x0efRRUAe\x80t.V",
	"BAM\x01\x00\x00\x00\x00\x00\x00\x00\x00000\xbf" +
		"e.Ce&Cell",
	"BAM\x01\x00\x00\x00\x00\x00\x00\x00\x00000\xe9",

	// Zero size buffer.
	"BAM\x01\x00\x00\x00\x00\x00\x9f7R",
	// Negative size buffer
	"BAM\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",

	// Short reads.
	"BAM\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x000",
	"BAM\x01\x00\x00\x00\x00\x00\x00\x00\x00\b\x00\x00\x000000" +
		"0000",

	// Invalid aux tags.
	"BAM\x01\x00\x00\x00\x00\x00\x00\x00\x00\x1c\x00\x00\x000000" +
		"000000000000000\xbd0000" +
		"0000",
	"BAM\x01\x00\x00\x00\x00\x00\x00\x00\x00-\x00\x00\x000000" +
		"0000\x04000\x01\x0000\x00\x00\x00\x000000" +
		"000000000000000000\x000" +
		"0",
	"BAM\x01\x00\x00\x00\x00\x00\x00\x00\x00-\x00\x00\x00 \r\n\xbd" +
		"\xff\xff\xff\xff\aun\x00\x00\x00\u007f\x00\x00\x00\x00\x00\x00\u007f\x00\x00" +
		"\x00 \r\n\xbd\xffxpect\\<invalid" +
		" ",
	"BAM\x01\x00\x00\x00\x00\x00\x00\x00\x00-\x00\x00\x000000" +
		"0000\x01000\x00\x0000\x00\x00\x00\x000000" +
		"00000000000000000000" +
		"0",
	"BAM\x01\x00\x00\x00\x00\x00\x00\x00\x00-\x00\x00\x000000" +
		"0000\x06000\x01\x0000\x00\x00\x00\x000000" +
		"00000000000000000000" +
		"n",
	"BAM\x01\x00\x00ewline;�\xbf\xb2\x10e" +
		"�\x16\x00\x00\x00\x00\x00\xbf89rf",
	"BAM\x01\x00\x00\x00\x00\x00\x00\x00\x00-\x00\x00\x000000" +
		"0000\x01000\x00\x0000\x00\x00\x00\x000000" +
		"00000000000\\00000000" +
		"0",
	"BAM\x01\x00\x00\x00\x00\x00\x00\x00\x00-\x00\x00\x000000" +
		"0000\a000\x00\x0000\x00\x00\x00\x000000" +
		"00000000000000000\b00" +
		"0",
	"BAM\x01\x00\x00ewline;�\xbf\xb2\x10e" +
		"�\x16\x00\x00\x00\x00\x00\xbf89rf",

	// lSeq overflow.
	"BAM\x01\x00\x00\x00\x00\x00\x00\x00\x000\x00\x00\x000000" +
		"000000000000\xff\xff\xff\u007f0000" +
		"00000000000000000000" +
		"00000000",

	// B-type aux data length overflow.
	"BAM\x01\x00\x00\x00\x00\x00\x00\x00\x000\x00\x00\x000000" +
		"0000\x01000\x01\x0000\x00\x00\x00\x000000" +
		"000000000000000BC000" +
		"00000000",
	"BAM\x01\x00\x00\x00\x00\x00\x00\x00\x000\x00\x00\x000000" +
		"0000\x02000\x01\x0000\x00\x00\x00\x000000" +
		"0000000000000000BB00" +
		"00000000000000000000",
}

func TestFuzzCrashers(t *testing.T) {
	for i, test := range fuzzCrashers {
		func() {
			i := i
			defer func() {
				r := recover()
				if r != nil {
					t.Errorf("unexpected panic for crasher %d: %v", i, r)
				}
			}()
			var buf bytes.Buffer
			w := bgzf.NewWriter(&buf, 1)
			_, err := w.Write([]byte(test))
			if err != nil {
				t.Errorf("unexpected error preparing buffer: %v", err)
			}
			err = w.Close()
			if err != nil {
				t.Errorf("unexpected error closing buffer: %v", err)
			}

			r, err := NewReader(&buf, 1)
			if err != nil {
				return
			}
			for {
				_, err := r.Read()
				if err != nil {
					break
				}
			}
		}()
	}
}
