mirror of
https://github.com/wader/fq.git
synced 2024-11-27 14:14:58 +03:00
0f35fe48fd
Before an empty range could cause gaps to be devided instead of continous.
125 lines
2.3 KiB
Go
125 lines
2.3 KiB
Go
package ranges
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"golang.org/x/exp/slices"
|
|
)
|
|
|
|
func max(a, b int64) int64 {
|
|
if a < b {
|
|
return b
|
|
}
|
|
return a
|
|
}
|
|
|
|
func min(a, b int64) int64 {
|
|
if a > b {
|
|
return b
|
|
}
|
|
return a
|
|
}
|
|
|
|
type Range struct {
|
|
Start int64
|
|
Len int64
|
|
}
|
|
|
|
func (r Range) Stop() int64 { return r.Start + r.Len }
|
|
|
|
func (r Range) String() string { return fmt.Sprintf("%d:%d", r.Start, r.Len) }
|
|
|
|
func (r Range) IsZero() bool { return r.Start == 0 && r.Len == 0 }
|
|
|
|
func RangeFromString(s string) Range {
|
|
ps := strings.Split(s, ":")
|
|
start, _ := strconv.Atoi(ps[0])
|
|
l, _ := strconv.Atoi(ps[1])
|
|
return Range{Start: int64(start), Len: int64(l)}
|
|
}
|
|
|
|
func SliceFromString(s string) []Range {
|
|
rr := []Range{}
|
|
for _, p := range strings.Split(s, " ") {
|
|
if p == "" {
|
|
continue
|
|
}
|
|
rr = append(rr, RangeFromString(p))
|
|
}
|
|
return rr
|
|
}
|
|
|
|
func MinMax(a, b Range) Range {
|
|
minStart := min(a.Start, b.Start)
|
|
maxStop := max(a.Stop(), b.Stop())
|
|
return Range{Start: minStart, Len: maxStop - minStart}
|
|
}
|
|
|
|
// Gaps in ranges limited by total range
|
|
func Gaps(total Range, ranges []Range) []Range {
|
|
if len(ranges) == 0 {
|
|
return []Range{total}
|
|
}
|
|
|
|
slices.SortFunc(ranges, func(a, b Range) bool { return a.Start < b.Start })
|
|
|
|
// worst case ranges+1 gaps
|
|
merged := make([]Range, 0, len(ranges)+1)
|
|
var madded bool
|
|
var m Range
|
|
|
|
for i := 0; i < len(ranges); {
|
|
madded = false
|
|
m = ranges[i]
|
|
|
|
// skip empty ranges
|
|
if m.Len == 0 {
|
|
i++
|
|
madded = true
|
|
continue
|
|
}
|
|
|
|
j := i + 1
|
|
for ; j < len(ranges); j++ {
|
|
if m.Start <= ranges[j].Start && m.Stop()+1 >= ranges[j].Start {
|
|
if ranges[j].Stop() > m.Stop() {
|
|
m.Len = ranges[j].Stop() - m.Start
|
|
}
|
|
} else {
|
|
i = j
|
|
merged = append(merged, m)
|
|
madded = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if j >= len(ranges) {
|
|
break
|
|
}
|
|
}
|
|
|
|
if !madded {
|
|
merged = append(merged, m)
|
|
}
|
|
|
|
if len(merged) == 0 {
|
|
return []Range{total}
|
|
}
|
|
|
|
gaps := make([]Range, 0, len(merged))
|
|
if merged[0].Start != total.Start {
|
|
gaps = append(gaps, Range{Start: 0, Len: merged[0].Start})
|
|
}
|
|
for i := 0; i < len(merged)-1; i++ {
|
|
gaps = append(gaps, Range{Start: merged[i].Stop(), Len: merged[i+1].Start - merged[i].Stop()})
|
|
}
|
|
l := merged[len(merged)-1]
|
|
if l.Stop() != total.Stop() {
|
|
gaps = append(gaps, Range{Start: l.Stop(), Len: total.Stop() - l.Stop()})
|
|
}
|
|
|
|
return gaps
|
|
}
|