// Stitch produces a subsequence of the receiver defined by fs. The result is stored in the receiver // and all contributing sequences are modified. func (m *Multi) Stitch(fs feat.Set) error { ff := fs.Features() for _, f := range ff { if f.End() < f.Start() { return errors.New("multi: feature end < feature start") } } ff = append(fts(nil), ff...) sort.Sort(fts(ff)) var ( fsp = make(fts, 0, len(ff)) csp *ft ) for i, f := range ff { if s := f.Start(); i == 0 || s > csp.e { csp = &ft{s: s, e: f.End()} fsp = append(fsp, csp) } else { csp.e = max(csp.e, f.End()) } } return m.Compose(fsp) }
// TranscriptsOf scans a feat.Set and returns any Transcripts that it finds. func TranscriptsOf(s feat.Set) []Transcript { var ts []Transcript for _, f := range s.Features() { if t, ok := f.(Transcript); ok { ts = append(ts, t) } } return ts }
// Compose produces a composition of src defined by the features in fs. The subparts of // the composition may be out of order and if features in fs specify orientation may be // reversed or reverse complemented depending on the src - if src is a SliceReverser and // its alphabet is a Complementor the segment will be reverse complemented, if the alphabte // is not a Complementor these segments will only be reversed. If src is not a SliceREverser // and a reverse segment is specified an error is returned. // Composing a circular sequence returns a linear sequence. func Compose(dst, src Sliceable, fs feat.Set) error { var ( sl = src.Slice() offset = src.Start() ff = feats(fs.Features()) ) pLen := sl.Len() end := pLen + offset t := make([]alphabet.Slice, len(ff)) var tl int for i, f := range ff { if f.End() < f.Start() { return errors.New("sequtils: feature end < feature start") } l := min(f.End(), end) - max(f.Start(), offset) tl += l t[i] = sl.Make(l, l) t[i].Copy(sl.Slice(max(f.Start()-offset, 0), min(f.End()-offset, pLen))) } c := sl.Make(0, tl) var r SliceReverser for i, ts := range t { if f, ok := ff[i].(feat.Orienter); ok && f.Orientation() == feat.Reverse { switch src := src.(type) { case SliceReverser: if r == nil { r = src.New().(SliceReverser) if _, ok := src.Alphabet().(alphabet.Complementor); ok { r.SetAlphabet(src.Alphabet()) r.SetSlice(ts) r.RevComp() } else { r.SetSlice(ts) r.Reverse() } } default: return errors.New("sequtils: unable to reverse segment during compose") } c = c.Append(r.Slice()) } else { c = c.Append(ts) } } dst.SetSlice(c) if dst, ok := dst.(seq.ConformationSetter); ok { dst.SetConformation(feat.Linear) } dst.SetOffset(0) return nil }
// Stitch produces a subsequence of src defined by fs and places the the result in dst. // The subsequences are guaranteed to be in order and non-overlapping even if not provided as such. // Stitching a circular sequence returns a linear sequence. func Stitch(dst, src Sliceable, fs feat.Set) error { var ( sl = src.Slice() offset = src.Start() ff = feats(fs.Features()) ) for _, f := range ff { if f.End() < f.Start() { return errors.New("sequtils: feature end < feature start") } } ff = append(feats(nil), ff...) sort.Sort(ff) // FIXME Does not correctly deal with circular sequences and feature sets. // Range over ff if src is circular and and trunc at start and end, do checks to // see if feature splits on origin and rearrange tail to front. pLen := sl.Len() end := pLen + offset type fi struct{ s, e int } var ( fsp = make([]*fi, 0, len(ff)) csp *fi ) for i, f := range ff { if s := f.Start(); i == 0 || s > csp.e { csp = &fi{s: s, e: f.End()} fsp = append(fsp, csp) } else { csp.e = max(csp.e, f.End()) } } var l int for _, f := range fsp { l += max(0, min(f.e, end)-max(f.s, offset)) } t := sl.Make(0, l) for _, f := range fsp { fs, fe := max(f.s-offset, 0), min(f.e-offset, pLen) if fs >= fe { continue } t = t.Append(sl.Slice(fs, fe)) } dst.SetSlice(t) if dst, ok := dst.(seq.ConformationSetter); ok { dst.SetConformation(feat.Linear) } dst.SetOffset(0) return nil }