blob: 4ee73b995b2394fe36cfdc83e35ec1c6a9d1b114 [file] [log] [blame]
// Copyright ©2014 The bíogo Authors. All rights reserved.
// Copyright 2021 The Go 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 turner
import (
"sort"
"github.com/biogo/biogo/align/pals"
"github.com/biogo/biogo/feat"
"github.com/biogo/store/interval"
)
type pairings struct {
interval interval.IntRange
id uintptr
loc
data []*pals.Feature
}
type loc struct{ feat.Feature }
func (p *pairings) Start() int { return p.interval.Start }
func (p *pairings) End() int { return p.interval.End }
func (p *pairings) Len() int { return p.interval.End - p.interval.Start }
func (p *pairings) ID() uintptr { return p.id }
func (p *pairings) Range() interval.IntRange { return p.interval }
func (p *pairings) Overlap(b interval.IntRange) bool {
return b.End >= p.Start() && b.Start <= p.End()
}
type byDepth []*pairings
func (p byDepth) Len() int { return len(p) }
func (p byDepth) Less(i, j int) bool {
return len(p[i].data) > len(p[j].data)
}
func (p byDepth) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
type simple interval.IntRange
func (i simple) Range() interval.IntRange { return interval.IntRange(i) }
func (i simple) ID() uintptr { return 0 }
func (i simple) Overlap(b interval.IntRange) bool {
return b.End >= i.Start && b.Start <= i.End
}
// Cluster performs a clustering of images in the pile p to create a collection
// of piles where image ends are within h times the images length of the ends of
// the generated sub-pile. Clustering is seeded by the set of unique image intervals
// in order of decreasing depth.
func Cluster(p *pals.Pile, h float64) []*pals.Pile {
pm := make(map[interval.IntRange][]*pals.Feature)
for _, fv := range p.Images {
i := interval.IntRange{Start: fv.Start(), End: fv.End()}
c := pm[i]
c = append(c, fv)
pm[i] = c
}
pl := make([]*pairings, 0, len(pm))
for iv, data := range pm {
pl = append(pl, &pairings{interval: iv, loc: loc{p}, data: data})
}
var t interval.IntTree
for i, pe := range pl {
pe.id = uintptr(i)
t.Insert(pe, true)
}
t.AdjustRanges()
var cl []*pals.Pile
sort.Sort(byDepth(pl))
for _, pe := range pl {
if _, ok := pm[interval.IntRange{Start: pe.Start(), End: pe.End()}]; !ok {
continue
}
thr := int(h * float64(pe.interval.End-pe.interval.Start))
from := pe.End()
to := pe.Start()
var (
mf []*pals.Feature
dm []interval.IntInterface
)
t.DoMatching(func(iv interval.IntInterface) (done bool) {
r := iv.Range()
if abs(pe.Start()-r.Start) <= thr && abs(pe.End()-r.End) <= thr {
ivp := iv.(*pairings)
dm = append(dm, iv)
mf = append(mf, ivp.data...)
if r.Start < from {
from = r.Start
}
if r.End > to {
to = r.End
}
}
return
}, simple{pe.Start() - thr, pe.End() + thr})
if len(mf) == 0 {
continue
}
for _, de := range dm {
t.Delete(de, true)
delete(pm, de.Range())
}
t.AdjustRanges()
cl = append(cl, &pals.Pile{
From: from,
To: to,
Loc: pe.Location(),
Images: mf,
})
}
return cl
}
func abs(a int) int {
if a != 0 && a == -a {
panic("weird number")
}
if a < 0 {
return -a
}
return a
}
// Range returns the start and end positions for all the members of p.
func Range(p []*pals.Pile) (start, end int) {
if len(p) == 0 {
return
}
start, end = p[0].Start(), p[0].End()
for _, pi := range p[1:] {
if s := pi.Start(); s < start {
start = s
}
if e := pi.End(); e > end {
end = e
}
}
return start, end
}
// Volume returns the number of bases contributing to the pile p.
func Volume(p *pals.Pile) int {
var m int
for _, im := range p.Images {
m += im.Len()
}
return m
}
// ByVolume allows a collection of pals.Pile to be sorted by volume.
type ByVolume []*pals.Pile
func (p ByVolume) Len() int { return len(p) }
func (p ByVolume) Less(i, j int) bool { return Volume(p[i]) > Volume(p[j]) }
func (p ByVolume) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
// ByVolume allows a collection of pals.Pile to be sorted by the number
// images in each pile.
type ByDepth []*pals.Pile
func (p ByDepth) Len() int { return len(p) }
func (p ByDepth) Less(i, j int) bool { return len(p[i].Images) > len(p[j].Images) }
func (p ByDepth) Swap(i, j int) { p[i], p[j] = p[j], p[i] }