func ReadBaseStrandTsv(r io.Reader) ([]BaseStrandTsvRow, error) {
	tsvReader := tsv.NewReader(r)
	tsvReader.Comment = '#'
	rows := make([]BaseStrandTsvRow, 0)
	for {
		var row BaseStrandTsvRow
		if err := tsvReader.Read(&row); err != nil {
			if err == io.EOF {
				break
			} else {
				return nil, err
			}
		}
		rows = append(rows, row)
	}
	return rows, nil
}
ReadBaseStrandTsv reads a basestrand.tsv file from the given io.Reader.
package snp
import (
	"bytes"
	"context"
	"encoding/binary"
	"fmt"
	"io"
	"strconv"
	"strings"
	"sync"
	"github.com/grailbio/base/file"
	"github.com/grailbio/base/log"
	"github.com/grailbio/base/recordio"
	"github.com/grailbio/base/recordio/recordiozstd"
	"github.com/grailbio/base/tsv"
	"github.com/grailbio/bio/pileup"
)
const (
	refNamesHeader = "RefNames"
	trailerVersion = 1
)
type BaseStrandPile struct {
	RefID  uint32
	Pos    uint32
	Counts [pileup.NBase][2]uint32
}
func WriteBaseStrandsRio(piles []BaseStrandPile, refNames []string, out io.Writer) error {
	recordWriter := recordio.NewWriter(out, recordio.WriterOpts{
		Marshal:      marshalBaseStrand,
		Transformers: []string{recordiozstd.Name},
	})
	recordWriter.AddHeader(refNamesHeader, strings.Join(refNames, "\000"))
	recordWriter.AddHeader(recordio.KeyTrailer, true)
	for i := range piles {
		recordWriter.Append(&piles[i])
	}
	recordWriter.SetTrailer(baseStrandsRioTrailer(len(piles)))
	return recordWriter.Finish()
}
func baseStrandsRioTrailer(numPiles int) []byte {
	var buffer bytes.Buffer
	if err := binary.Write(&buffer, binary.LittleEndian, int64(trailerVersion)); err != nil {
		panic("couldn't write trailer version")
	}
	if err := binary.Write(&buffer, binary.LittleEndian, int64(numPiles)); err != nil {
		panic("couldn't write numPiles to trailer")
	}
	return buffer.Bytes()
}
func parseBaseStrandsTrailer(trailer []byte) (int64, error) {
	r := bytes.NewReader(trailer)
	var version, numPiles int64
	if err := binary.Read(r, binary.LittleEndian, &version); err != nil {
		return 0, err
	}
	if version != trailerVersion {
		return 0, fmt.Errorf("unrecognized trailer version: got %d, want %d", version, trailerVersion)
	}
	if err := binary.Read(r, binary.LittleEndian, &numPiles); err != nil {
		return 0, err
	}
	return numPiles, nil
}
func marshalBaseStrand(scratch []byte, p interface{}) ([]byte, error) {
	t := scratch
	if len(t) < 40 {
		t = make([]byte, 40)
	}
	t = t[:40]
	pile := p.(*BaseStrandPile)
	binary.LittleEndian.PutUint32(t[:4], pile.RefID)
	binary.LittleEndian.PutUint32(t[4:8], pile.Pos)
	binary.LittleEndian.PutUint32(t[8:12], pile.Counts[pileup.BaseA][0])
	binary.LittleEndian.PutUint32(t[12:16], pile.Counts[pileup.BaseA][1])
	binary.LittleEndian.PutUint32(t[16:20], pile.Counts[pileup.BaseC][0])
	binary.LittleEndian.PutUint32(t[20:24], pile.Counts[pileup.BaseC][1])
	binary.LittleEndian.PutUint32(t[24:28], pile.Counts[pileup.BaseG][0])
	binary.LittleEndian.PutUint32(t[28:32], pile.Counts[pileup.BaseG][1])
	binary.LittleEndian.PutUint32(t[32:36], pile.Counts[pileup.BaseT][0])
	binary.LittleEndian.PutUint32(t[36:40], pile.Counts[pileup.BaseT][1])
	return t, nil
}
func ReadBaseStrandsRio(rs io.ReadSeeker) (piles []BaseStrandPile, refNames []string, err error) {
	var unmarshaller BaseStrandUnmarshaller
	scanner := recordio.NewScanner(rs, recordio.ScannerOpts{
		Unmarshal: unmarshaller.UnmarshalBaseStrand,
	})
	if len(scanner.Trailer()) != 0 {
		var numPiles int64
		if numPiles, err = parseBaseStrandsTrailer(scanner.Trailer()); err != nil {
			return
		}
		unmarshaller.init(numPiles)
	}
	hdr := scanner.Header()
	for _, kv := range hdr {
		switch kv.Key {
		case refNamesHeader:
			packedRefNames := kv.Value.(string)
			refNames = strings.Split(packedRefNames, "\000")
		}
	}
	for scanner.Scan() {
		piles = append(piles, *scanner.Get().(*BaseStrandPile))
	}
	err = scanner.Err()
	return
}
type BaseStrandUnmarshaller struct {
	piles  []BaseStrandPile
	offset int
}
func (b *BaseStrandUnmarshaller) init(size int64) {
	if b.piles != nil {
		panic("tried to initialize when already initialized")
	}
	b.piles = make([]BaseStrandPile, size)
}
func (b *BaseStrandUnmarshaller) UnmarshalBaseStrand(in []byte) (out interface{}, err error) {
	in = in[:40]  
	if b.offset == len(b.piles) {
		b.piles = append(b.piles, BaseStrandPile{})
	}
	pile := &b.piles[b.offset]
	b.offset++
	pile.RefID = binary.LittleEndian.Uint32(in[:4])
	pile.Pos = binary.LittleEndian.Uint32(in[4:8])
	pile.Counts[pileup.BaseA][0] = binary.LittleEndian.Uint32(in[8:12])
	pile.Counts[pileup.BaseA][1] = binary.LittleEndian.Uint32(in[12:16])
	pile.Counts[pileup.BaseC][0] = binary.LittleEndian.Uint32(in[16:20])
	pile.Counts[pileup.BaseC][1] = binary.LittleEndian.Uint32(in[20:24])
	pile.Counts[pileup.BaseG][0] = binary.LittleEndian.Uint32(in[24:28])
	pile.Counts[pileup.BaseG][1] = binary.LittleEndian.Uint32(in[28:32])
	pile.Counts[pileup.BaseT][0] = binary.LittleEndian.Uint32(in[32:36])
	pile.Counts[pileup.BaseT][1] = binary.LittleEndian.Uint32(in[36:40])
	return pile, nil
}
func WriteBaseStrandToTSV(piles []BaseStrandPile, refNames []string, w io.Writer) (err error) {
	outTSV := tsv.NewWriter(w)
	outTSV.WriteString("CHROM\tPOS\tA+\tA-\tC+\tC-\tG+\tG-\tT+\tT-")
	if err = outTSV.EndLine(); err != nil {
		return
	}
	for _, pile := range piles {
		outTSV.WriteString(refNames[pile.RefID])
		outTSV.WriteUint32(pile.Pos)
		outTSV.WriteUint32(pile.Counts[pileup.BaseA][0])
		outTSV.WriteUint32(pile.Counts[pileup.BaseA][1])
		outTSV.WriteUint32(pile.Counts[pileup.BaseC][0])
		outTSV.WriteUint32(pile.Counts[pileup.BaseC][1])
		outTSV.WriteUint32(pile.Counts[pileup.BaseG][0])
		outTSV.WriteUint32(pile.Counts[pileup.BaseG][1])
		outTSV.WriteUint32(pile.Counts[pileup.BaseT][0])
		outTSV.WriteUint32(pile.Counts[pileup.BaseT][1])
		if err = outTSV.EndLine(); err != nil {
			return
		}
	}
	if err = outTSV.Flush(); err != nil {
		return
	}
	return
}
func WriteBaseStrandsRioAsTSV(ctx context.Context, path string, w io.Writer) error {
	file, err := file.Open(ctx, path)
	if err != nil {
		return err
	}
	piles, refNames, err2 := ReadBaseStrandsRio(file.Reader(ctx))
	if err2 != nil {
		return err2
	}
	return WriteBaseStrandToTSV(piles, refNames, w)
}
type BaseStrandTsvRow struct {
	Chr  string `tsv:"#CHROM"`  
	Pos  int64  `tsv:"POS"`     
	Ref  string `tsv:"REF"`     
	FwdA int64  `tsv:"A+"`      
	RevA int64  `tsv:"A-"`      
	FwdC int64  `tsv:"C+"`      
	RevC int64  `tsv:"C-"`      
	FwdG int64  `tsv:"G+"`      
	RevG int64  `tsv:"G-"`      
	FwdT int64  `tsv:"T+"`      
	RevT int64  `tsv:"T-"`      
}