github.com/biogo/biogo@v1.0.4/align/pals/filter/merge.go (about) 1 // Copyright ©2011-2013 The bíogo Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package filter 6 7 import ( 8 "github.com/biogo/biogo/alphabet" 9 "github.com/biogo/biogo/index/kmerindex" 10 "github.com/biogo/biogo/seq/linear" 11 12 "sort" 13 ) 14 15 const ( 16 diagonalPadding = 2 17 ) 18 19 // A Merger aggregates and clips an ordered set of trapezoids. 20 type Merger struct { 21 target, query *linear.Seq 22 filterParams *Params 23 maxIGap int 24 leftPadding, bottomPadding int 25 binWidth int 26 selfComparison bool 27 freeTraps, trapList *trapezoid 28 trapOrder, tail *trapezoid 29 eoTerm *trapezoid 30 trapCount int 31 valueToCode alphabet.Index 32 } 33 34 // Create a new Merger using the provided kmerindex, query sequence, filter parameters and maximum inter-segment gap length. 35 // If selfCompare is true only the upper diagonal of the comparison matrix is examined. 36 func NewMerger(ki *kmerindex.Index, query *linear.Seq, filterParams *Params, maxIGap int, selfCompare bool) *Merger { 37 tubeWidth := filterParams.TubeOffset + filterParams.MaxError 38 binWidth := tubeWidth - 1 39 leftPadding := diagonalPadding + binWidth 40 41 eoTerm := &trapezoid{Trapezoid: Trapezoid{ 42 Left: query.Len() + 1 + leftPadding, 43 Right: query.Len() + 1, 44 Bottom: -1, 45 Top: query.Len() + 1, 46 }} 47 48 return &Merger{ 49 target: ki.Seq(), 50 filterParams: filterParams, 51 maxIGap: maxIGap, 52 query: query, 53 selfComparison: selfCompare, 54 bottomPadding: ki.K() + 2, 55 leftPadding: leftPadding, 56 binWidth: binWidth, 57 eoTerm: eoTerm, 58 trapOrder: eoTerm, 59 valueToCode: ki.Seq().Alpha.LetterIndex(), 60 } 61 } 62 63 // Merge a filter hit into the collection. 64 func (m *Merger) MergeFilterHit(h *Hit) { 65 Left := -h.Diagonal 66 if m.selfComparison && Left <= m.filterParams.MaxError { 67 return 68 } 69 Top := h.To 70 Bottom := h.From 71 72 var temp, free *trapezoid 73 for base := m.trapOrder; ; base = temp { 74 temp = base.next 75 switch { 76 case Bottom-m.bottomPadding > base.Top: 77 if free == nil { 78 m.trapOrder = temp 79 } else { 80 free.join(temp) 81 } 82 m.trapList = base.join(m.trapList) 83 m.trapCount++ 84 case Left-diagonalPadding > base.Right: 85 free = base 86 case Left+m.leftPadding >= base.Left: 87 if Left+m.binWidth > base.Right { 88 base.Right = Left + m.binWidth 89 } 90 if Left < base.Left { 91 base.Left = Left 92 } 93 if Top > base.Top { 94 base.Top = Top 95 } 96 97 if free != nil && free.Right+diagonalPadding >= base.Left { 98 free.Right = base.Right 99 if free.Bottom > base.Bottom { 100 free.Bottom = base.Bottom 101 } 102 if free.Top < base.Top { 103 free.Top = base.Top 104 } 105 106 free.join(temp) 107 m.freeTraps = base.join(m.freeTraps) 108 } else if temp != nil && temp.Left-diagonalPadding <= base.Right { 109 base.Right = temp.Right 110 if base.Bottom > temp.Bottom { 111 base.Bottom = temp.Bottom 112 } 113 if base.Top < temp.Top { 114 base.Top = temp.Top 115 } 116 base.join(temp.next) 117 m.freeTraps = temp.join(m.freeTraps) 118 temp = base.next 119 } 120 121 return 122 default: 123 if m.freeTraps == nil { 124 m.freeTraps = &trapezoid{} 125 } 126 if free == nil { 127 m.trapOrder = m.freeTraps 128 } else { 129 free.join(m.freeTraps) 130 } 131 132 free, m.freeTraps = m.freeTraps.decapitate() 133 free.join(base) 134 135 free.Top = Top 136 free.Bottom = Bottom 137 free.Left = Left 138 free.Right = Left + m.binWidth 139 140 return 141 } 142 } 143 } 144 145 func (m *Merger) clipVertical() { 146 for base := m.trapList; base != nil; base = base.next { 147 lagPosition := base.Bottom - m.maxIGap + 1 148 if lagPosition < 0 { 149 lagPosition = 0 150 } 151 lastPosition := base.Top + m.maxIGap 152 if lastPosition > m.query.Len() { 153 lastPosition = m.query.Len() 154 } 155 156 var pos int 157 for pos = lagPosition; pos < lastPosition; pos++ { 158 if m.valueToCode[m.query.Seq[pos]] >= 0 { 159 if pos-lagPosition >= m.maxIGap { 160 if lagPosition-base.Bottom > 0 { 161 if m.freeTraps == nil { 162 m.freeTraps = &trapezoid{} 163 } 164 165 m.freeTraps = m.freeTraps.prependFrontTo(base) 166 167 base.Top = lagPosition 168 base = base.next 169 base.Bottom = pos 170 m.trapCount++ 171 } else { 172 base.Bottom = pos 173 } 174 } 175 lagPosition = pos + 1 176 } 177 } 178 if pos-lagPosition >= m.maxIGap { 179 base.Top = lagPosition 180 } 181 } 182 } 183 184 func (m *Merger) clipTrapezoids() { 185 for base := m.trapList; base != nil; base = base.next { 186 if base.Top-base.Bottom < m.bottomPadding-2 { 187 continue 188 } 189 190 aBottom := base.Bottom - base.Right 191 aTop := base.Top - base.Left 192 193 lagPosition := aBottom - m.maxIGap + 1 194 if lagPosition < 0 { 195 lagPosition = 0 196 } 197 lastPosition := aTop + m.maxIGap 198 if lastPosition > m.target.Len() { 199 lastPosition = m.target.Len() 200 } 201 202 lagClip := aBottom 203 var pos int 204 for pos = lagPosition; pos < lastPosition; pos++ { 205 if m.valueToCode[m.target.Seq[pos]] >= 0 { 206 if pos-lagPosition >= m.maxIGap { 207 if lagPosition > lagClip { 208 if m.freeTraps == nil { 209 m.freeTraps = &trapezoid{} 210 } 211 212 m.freeTraps = m.freeTraps.prependFrontTo(base) 213 214 base.clip(lagPosition, lagClip) 215 216 base = base.next 217 m.trapCount++ 218 } 219 lagClip = pos 220 } 221 lagPosition = pos + 1 222 } 223 } 224 225 if pos-lagPosition < m.maxIGap { 226 lagPosition = aTop 227 } 228 229 base.clip(lagPosition, lagClip) 230 231 m.tail = base 232 } 233 } 234 235 // Finalise the merged collection and return a sorted slice of Trapezoids. 236 func (m *Merger) FinaliseMerge() Trapezoids { 237 var next *trapezoid 238 for base := m.trapOrder; base != m.eoTerm; base = next { 239 next = base.next 240 m.trapList = base.join(m.trapList) 241 m.trapCount++ 242 } 243 244 m.clipVertical() 245 m.clipTrapezoids() 246 247 if m.tail != nil { 248 m.freeTraps = m.tail.join(m.freeTraps) 249 } 250 251 traps := make(Trapezoids, m.trapCount) 252 for i, z := 0, m.trapList; i < m.trapCount; i++ { 253 traps[i] = z.Trapezoid 254 z, z.next = z.next, nil 255 } 256 257 sort.Sort(traps) 258 259 return traps 260 }