func (d *dtw) prepareDistances(seq int, kmer uint16, pos, start, end int) (int, int) {
	centre := len(d.ds) / 2
	if start < 0 {
		start = 0
	}
	if end > len(d.ds) {
		end = len(d.ds)
	}
	seqStart := pos - centre + start  
	seqs,_ := d.measure.GetSequences()
	if seqStart < 0 {
		start -= seqStart  
		seqStart = 0
		if end < start {
			end = start
		}
	}
	if pos-centre+end >= len(seqs[seq]) {
		end = len(seqs[seq])-pos+centre
	}
	d.measure.Distances(kmer, seq, seqStart, d.ds[start:end])
	for i := 0; i < start; i++ {
		d.ds[i] = d.maxCost/4 
	}
	for i := end; i < len(d.ds); i++ {
		d.ds[i] = d.maxCost/4
	}
	exp := int(d.depth + d.expectedPositions[seq])
	for i := start; i < end; i++ {
		delta := (pos - centre + i) - exp
		if delta < -16 {
			d.ds[i] += uint16(-16 - delta)
		} else if delta > 16 {
			d.ds[i] += uint16(delta - 16)
		}
	}
	return start, end
}
pos: sequence position at centre of offsets
start/end: region of offsets to get distances for, to be written to d.ds at same locations
returns: final start,end region used -- always with original start to end
package alignment
import (
	"fmt"
	"github.com/jteutenberg/downpore/sequence"
	"github.com/jteutenberg/downpore/util"
	"math"
	"sort"
)
var initialOffset = int32(8)
var debug = false
type Result struct {
	EndPositions []int32
}
type Aligner interface {
	GlobalConsensus() (<-chan uint16, <-chan *QualityMetrics, <-chan *Result)
	GlobalAlignment() (<-chan uint16, <-chan *QualityMetrics, <-chan []int)
	GlobalAlignmentTo([]uint16) (<-chan uint16, <-chan *QualityMetrics, <-chan []int)
	ConsensusCost([]uint16) uint
}
type Measure interface {
	SetSequences(seqs [][]uint16, rc []bool)
	GetSequences() ([][]uint16, []bool)
	GetSequenceLen(int) int
	Distances(a uint16, sequence, start int, ds []uint16)
}
type QualityMetrics struct {
	ExactFraction  float64
	CostDelta      uint
	StateSpaceSize int
}
type dtw struct {
	maxWarp        int  
	maxCost        uint16
	initialGapCost uint16
	costThreshold  uint16  
	measure        Measure
	full           bool  
	k                 int  
	prevKmers         *util.IntSet
	kMask             uint16
	ds                []uint16  
	landmarks         []*landmark
	expectedPositions []int32
	depth             int32
}
type landmark struct {
	k         uint16
	cost      uint
	seqs      []bool   
	positions []int32  
}
type state struct {
	k            uint16      
	positions    []int32     
	offsets      [][]uint16  
	prev         *state      
	minCost      uint        
	votes        float64     
	spaceSize    int         
	finished     bool        
	nextLandmark int         
	quality      []float64   
}
func (s *state) printOffsets() {
	for j, offsets := range s.offsets {
		for _, off := range offsets {
			if off > 30000 {
				fmt.Print("- ")
			} else {
				fmt.Print(off, " ")
			}
		}
		fmt.Println("\n", s.positions[j])
	}
}
func NewDTWAligner(maxWarp int, initialGapCost uint, measure Measure, full bool, costThreshold uint, k int) Aligner {
	for maxWarp%8 != 0 {
		maxWarp++
	}
	ks := k
	var kMask uint16
	for k > 0 {
		kMask = (kMask << 2) | 0x3
		k--
	}
	d := dtw{maxWarp * 2, uint16(math.MaxUint16 / 2), uint16(initialGapCost), uint16(costThreshold), measure, full, ks, util.NewIntSet(), kMask, make([]uint16, maxWarp*2, maxWarp*2), make([]*landmark, 0, 10), nil, 0}
	return &d
}
func (lm *landmark) matchesLandmark(positions []int32) bool {
	count := 0
	c2 := 0
	for i, use := range lm.seqs {
		if use {
			c2++
			if positions[i] == lm.positions[i] {
				count++
			}
		}
	}
	return count >= c2/2
}
func (lm *landmark) matches(positions []int32) bool {
	count := 0
	c2 := 0
	for i, use := range lm.seqs {
		if use {
			c2++
			if positions[i] >= lm.positions[i]-6 && positions[i] <= lm.positions[i]+6 {
				count++
			}
		}
	}
	return count >= c2/2
}
func (lm *landmark) isPriorTo(positions []int32) bool {
	for i, use := range lm.seqs {
		if use && positions[i]-4 < lm.positions[i] {
			return false
		}
	}
	return true
}
func (lm *landmark) isPriorLandmarkTo(otherSeqs []bool, otherPositions []int32) bool {
	for i, use := range lm.seqs {
		if use && otherSeqs[i] && otherPositions[i] < lm.positions[i] {
			return false
		}
	}
	return true
}
func (lm *landmark) lockState(s *state, seqs [][]uint16, maxCost uint16) {
	centre := int32(len(s.offsets[0]) / 2)
	for j, p := range lm.positions {
		if !lm.seqs[j] || p < s.positions[j]-centre {
			continue
		}
		seq := seqs[j]
		offs := s.offsets[j]
		newMin := maxCost
		start := int(s.positions[j] - centre)
		for n := 0; n < len(offs); n++ {
			off := offs[n]
			ip := start + n
			if off < maxCost && ip >= 0 && ip < len(seq) {
				if seq[ip] != lm.k {
					offs[n] = maxCost
				} else if off < newMin {
					newMin = off
				}
			}
		}
		for n := 0; n < len(offs); n++ {
			if offs[n] < maxCost {
				offs[n] -= newMin
			}
		}
		s.minCost += uint(newMin)
	}
}
func (lm *landmark) cropState(s *state, seqs [][]uint16, maxCost uint16) {
	centre := int32(len(s.offsets[0]) / 2)
	for j, p := range lm.positions {
		if !lm.seqs[j] {
			continue
		}
		pos := int(s.positions[j]-centre)
		p -= int32(pos)
		offs := s.offsets[j]
		if p >= int32(len(offs)) || p < 0 {
			continue  
		}
		for n := 0; n < int(p); n++ {
			if n+pos < 0 || seqs[j][n+pos] != lm.k {
				offs[n] = maxCost
			} else {
				p = int32(n)
				break
			}
		}
		newMin := maxCost
		for n := int(p); n < len(offs); n++ {
			if offs[n] < newMin {
				newMin = offs[n]
			}
		}
		s.minCost += uint(newMin)
		for n := int(p); n < len(offs); n++ {
			if offs[n] < maxCost {
				offs[n] -= newMin
			}
		}
	}
}
func fixDrift(s *state, bestPos, index int, maxCost uint16) int {
	offs := s.offsets[index]
	centre := len(offs) / 2
	drift := centre - bestPos
	if drift < -4 {
		for i := 0; i < len(offs)+drift; i++ {
			offs[i] = offs[i-drift]
		}
		s.positions[index] -= int32(drift)
		for i := len(offs) + drift; i < len(offs); i++ {
			offs[i] = maxCost
		}
	} else if drift > 4 {
		for i := len(offs) - 1; i >= drift; i-- {
			offs[i] = offs[i-drift]
		}
		s.positions[index] -= int32(drift)
		for i := 0; i < drift; i++ {
			offs[i] = maxCost
		}
	} else {
		return 0
	}
	return drift
}
func updateOffsetsAsm(ds, poffs, offsets []uint16, threshold uint16) uint16