Added inclusive / exclusive functionality
This commit is contained in:
parent
6fdaa341ff
commit
6702d50231
2
.gitignore
vendored
2
.gitignore
vendored
@ -4,4 +4,4 @@
|
|||||||
|
|
||||||
debug.test
|
debug.test
|
||||||
example/debug
|
example/debug
|
||||||
example/out.png
|
example/*.png
|
||||||
|
@ -6,4 +6,5 @@
|
|||||||
20,30
|
20,30
|
||||||
25,28
|
25,28
|
||||||
30,32
|
30,32
|
||||||
|
0,1
|
||||||
10,12
|
10,12
|
140
example/main.go
140
example/main.go
@ -21,6 +21,26 @@ const (
|
|||||||
MaxX = 40
|
MaxX = 40
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type PlotType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
PlotTypeIntervals PlotType = iota
|
||||||
|
PlotTypeGaps
|
||||||
|
PlotTypeOverlapped
|
||||||
|
PlotTypeMerged
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
openBracket = "["
|
||||||
|
closeBracket = "]"
|
||||||
|
openParenthesis = "("
|
||||||
|
closeParenthesis = ")"
|
||||||
|
intervalOpening = openBracket
|
||||||
|
intervalClosing = closeBracket
|
||||||
|
lowInclusive bool
|
||||||
|
highInclusive bool
|
||||||
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
filename := "data.txt"
|
filename := "data.txt"
|
||||||
xys, err := readData(filename)
|
xys, err := readData(filename)
|
||||||
@ -47,12 +67,17 @@ func readData(path string) ([]xy, error) {
|
|||||||
// read line by line using a scanner (because we don't know if the file will be huge)
|
// read line by line using a scanner (because we don't know if the file will be huge)
|
||||||
s := bufio.NewScanner(f)
|
s := bufio.NewScanner(f)
|
||||||
for s.Scan() {
|
for s.Scan() {
|
||||||
var x, y int
|
var low, high int
|
||||||
_, err := fmt.Sscanf(s.Text(), "%d,%d", &x, &y)
|
_, err := fmt.Sscanf(s.Text(), "%d,%d", &low, &high)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("discarding bad data point %v: %v", s.Text(), err)
|
log.Printf("discarding bad data point %v: %v", s.Text(), err)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
xys = append(xys, xy{x, y})
|
if low > high {
|
||||||
|
log.Printf("discarding bad data point (low, high)=(%v): low can not be greater than high", s.Text())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
xys = append(xys, xy{low, high})
|
||||||
}
|
}
|
||||||
if err := s.Err(); err != nil {
|
if err := s.Err(); err != nil {
|
||||||
return nil, fmt.Errorf("could not scan: %v", err)
|
return nil, fmt.Errorf("could not scan: %v", err)
|
||||||
@ -61,7 +86,24 @@ func readData(path string) ([]xy, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func initIntervals(xys []xy) interval.Intervals {
|
func initIntervals(xys []xy) interval.Intervals {
|
||||||
intervals := interval.NewIntervals(MinX, MaxX)
|
intervals := interval.NewIntervals(MinX, MaxX, true, true)
|
||||||
|
|
||||||
|
if intervals.IsLowInclusive() {
|
||||||
|
intervalOpening = "["
|
||||||
|
lowInclusive = true
|
||||||
|
} else {
|
||||||
|
intervalOpening = "("
|
||||||
|
lowInclusive = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if intervals.IsHighInclusive() {
|
||||||
|
intervalClosing = "]"
|
||||||
|
highInclusive = true
|
||||||
|
} else {
|
||||||
|
intervalClosing = ")"
|
||||||
|
highInclusive = false
|
||||||
|
}
|
||||||
|
|
||||||
for _, xy := range xys {
|
for _, xy := range xys {
|
||||||
intervals.Add(&interval.Interval{Low: xy.x, High: xy.y})
|
intervals.Add(&interval.Interval{Low: xy.x, High: xy.y})
|
||||||
}
|
}
|
||||||
@ -93,7 +135,7 @@ func alignPlots(plotItems []*plot.Plot) *vgimg.Canvas {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
img := vgimg.New(vg.Points(512), vg.Points(float64(128*rows)))
|
img := vgimg.New(vg.Points(512), vg.Points(float64(200*rows)))
|
||||||
dc := draw.New(img)
|
dc := draw.New(img)
|
||||||
|
|
||||||
t := draw.Tiles{
|
t := draw.Tiles{
|
||||||
@ -129,7 +171,7 @@ func createFileFromCanvas(path string, img *vgimg.Canvas) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createPlot(title string, xys plotter.XYs) (*plot.Plot, error) {
|
func createPlot(title string, xys plotter.XYs, plotType PlotType) (*plot.Plot, error) {
|
||||||
p, err := plot.New()
|
p, err := plot.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not create plot: %v", err)
|
return nil, fmt.Errorf("could not create plot: %v", err)
|
||||||
@ -142,7 +184,7 @@ func createPlot(title string, xys plotter.XYs) (*plot.Plot, error) {
|
|||||||
// p.X.Label.Text = "values"
|
// p.X.Label.Text = "values"
|
||||||
p.X.Padding = vg.Length(5)
|
p.X.Padding = vg.Length(5)
|
||||||
p.Y.Padding = vg.Length(20)
|
p.Y.Padding = vg.Length(20)
|
||||||
plotIntervals(p, xys)
|
plotIntervals(p, plotType, xys)
|
||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,7 +193,7 @@ func plotData(path string, intervals interval.Intervals) error {
|
|||||||
|
|
||||||
// create Intervals plot
|
// create Intervals plot
|
||||||
xysIntervals := convertToPlotterXYs(intervals.Get())
|
xysIntervals := convertToPlotterXYs(intervals.Get())
|
||||||
p1, err := createPlot("Intervals", xysIntervals)
|
p1, err := createPlot("Intervals", xysIntervals, PlotTypeIntervals)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not create plot: %v", err)
|
return fmt.Errorf("could not create plot: %v", err)
|
||||||
}
|
}
|
||||||
@ -159,7 +201,7 @@ func plotData(path string, intervals interval.Intervals) error {
|
|||||||
|
|
||||||
// create Gaps plot
|
// create Gaps plot
|
||||||
xysGaps := convertToPlotterXYs(intervals.Gaps())
|
xysGaps := convertToPlotterXYs(intervals.Gaps())
|
||||||
p2, err := createPlot("Gaps", xysGaps)
|
p2, err := createPlot("Gaps", xysGaps, PlotTypeGaps)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not create plot: %v", err)
|
return fmt.Errorf("could not create plot: %v", err)
|
||||||
}
|
}
|
||||||
@ -167,7 +209,7 @@ func plotData(path string, intervals interval.Intervals) error {
|
|||||||
|
|
||||||
// create Overlapped `plot
|
// create Overlapped `plot
|
||||||
xysOverlapped := convertToPlotterXYs(intervals.Overlapped())
|
xysOverlapped := convertToPlotterXYs(intervals.Overlapped())
|
||||||
p3, err := createPlot("Overlapped", xysOverlapped)
|
p3, err := createPlot("Overlapped", xysOverlapped, PlotTypeOverlapped)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not create plot: %v", err)
|
return fmt.Errorf("could not create plot: %v", err)
|
||||||
}
|
}
|
||||||
@ -175,7 +217,7 @@ func plotData(path string, intervals interval.Intervals) error {
|
|||||||
|
|
||||||
// create Merged plot
|
// create Merged plot
|
||||||
xysMerged := convertToPlotterXYs(intervals.Merge())
|
xysMerged := convertToPlotterXYs(intervals.Merge())
|
||||||
p4, err := createPlot("Merged", xysMerged)
|
p4, err := createPlot("Merged", xysMerged, PlotTypeMerged)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not create plot: %v", err)
|
return fmt.Errorf("could not create plot: %v", err)
|
||||||
}
|
}
|
||||||
@ -190,16 +232,71 @@ func plotData(path string, intervals interval.Intervals) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func plotIntervals(p *plot.Plot, xys plotter.XYs) error {
|
func plotIntervals(p *plot.Plot, plotType PlotType, xys plotter.XYs) error {
|
||||||
var ps []plot.Plotter
|
var ps []plot.Plotter
|
||||||
colors := getColors()
|
colors := getColors()
|
||||||
numColors := len(colors)
|
numColors := len(colors)
|
||||||
|
|
||||||
|
crossShape := draw.CrossGlyph{}
|
||||||
|
ringShape := draw.RingGlyph{}
|
||||||
|
legendWithCrossShape := false
|
||||||
|
legendWithRingShape := false
|
||||||
for i, xy := range xys {
|
for i, xy := range xys {
|
||||||
label := fmt.Sprintf("(%v,%v)", xy.X, xy.Y)
|
pXYLow := struct{ X, Y float64 }{xy.X, float64(i)}
|
||||||
pXYs := plotter.XYs{{xy.X, float64(i)}, {xy.Y, float64(i)}}
|
pXYHigh := struct{ X, Y float64 }{xy.Y, float64(i)}
|
||||||
|
pXYs := plotter.XYs{pXYLow, pXYHigh}
|
||||||
color := colors[i%numColors]
|
color := colors[i%numColors]
|
||||||
|
|
||||||
s, err := plotter.NewScatter(pXYs)
|
var s1, s2 *plotter.Scatter
|
||||||
|
var err error
|
||||||
|
label := ""
|
||||||
|
if plotType == PlotTypeIntervals {
|
||||||
|
label = fmt.Sprintf("%s%v,%v%s", intervalOpening, xy.X, xy.Y, intervalClosing)
|
||||||
|
s1, err = plotter.NewScatter(plotter.XYs{pXYLow})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to create new scatter: %v", err)
|
||||||
|
}
|
||||||
|
if !lowInclusive {
|
||||||
|
s1.GlyphStyle.Shape = crossShape
|
||||||
|
if !legendWithCrossShape {
|
||||||
|
p.Legend.Add("Exclusive", s1)
|
||||||
|
legendWithCrossShape = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s1.GlyphStyle.Shape = ringShape
|
||||||
|
if !legendWithRingShape {
|
||||||
|
p.Legend.Add("Inclusive", s1)
|
||||||
|
legendWithRingShape = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s2, err = plotter.NewScatter(plotter.XYs{pXYHigh})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to create new scatter: %v", err)
|
||||||
|
}
|
||||||
|
if !highInclusive {
|
||||||
|
s2.GlyphStyle.Shape = crossShape
|
||||||
|
if !legendWithCrossShape {
|
||||||
|
p.Legend.Add("Exclusive", s2)
|
||||||
|
legendWithCrossShape = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s2.GlyphStyle.Shape = ringShape
|
||||||
|
if !legendWithRingShape {
|
||||||
|
p.Legend.Add("Inclusive", s2)
|
||||||
|
legendWithRingShape = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
label = fmt.Sprintf("%s%v,%v%s", openBracket, xy.X, xy.Y, closeBracket)
|
||||||
|
s1, err = plotter.NewScatter(pXYs)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to create new scatter: %v", err)
|
||||||
|
}
|
||||||
|
if !lowInclusive && !highInclusive {
|
||||||
|
s1.GlyphStyle.Shape = crossShape
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if xy.X != xy.Y {
|
if xy.X != xy.Y {
|
||||||
l, err := plotter.NewLine(pXYs)
|
l, err := plotter.NewLine(pXYs)
|
||||||
@ -211,15 +308,20 @@ func plotIntervals(p *plot.Plot, xys plotter.XYs) error {
|
|||||||
ps = append(ps, l)
|
ps = append(ps, l)
|
||||||
p.Legend.Add(label, l)
|
p.Legend.Add(label, l)
|
||||||
} else {
|
} else {
|
||||||
s.Color = color
|
s1.Color = color
|
||||||
p.Legend.Add(label, s)
|
if s2 != nil {
|
||||||
|
s2.Color = color
|
||||||
|
}
|
||||||
|
p.Legend.Add(label, s1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not create a new scatter: %v", err)
|
return fmt.Errorf("could not create a new scatter: %v", err)
|
||||||
}
|
}
|
||||||
ps = append(ps, s)
|
ps = append(ps, s1)
|
||||||
|
if s2 != nil {
|
||||||
|
ps = append(ps, s2)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
p.Legend.Left = false
|
p.Legend.Left = false
|
||||||
p.Add(ps...)
|
p.Add(ps...)
|
||||||
|
12
find.go
12
find.go
@ -1,14 +1,20 @@
|
|||||||
package interval
|
package interval
|
||||||
|
|
||||||
func (intvls *intervals) FindIntervalsForValue(value int) []*Interval {
|
func (intvls *intervals) FindIntervalsForValue(value int) []*Interval {
|
||||||
|
// sort intervals (if necessary)
|
||||||
intvls.Sort()
|
intvls.Sort()
|
||||||
|
|
||||||
var matches []*Interval
|
var matches []*Interval
|
||||||
for _, intvl := range intvls.Intervals {
|
for _, intvl := range intvls.Intervals {
|
||||||
if intvl.Low > value {
|
// convert if necessary exclusive low/high values into inclusive ones
|
||||||
// due to the intervals are sorted, we can confirm that we will not find more matches
|
low, high := intvls.getInclusives(intvl.Low, intvl.High)
|
||||||
|
|
||||||
|
// check if we have to stop searching
|
||||||
|
if low > value {
|
||||||
|
// due to the intervals are sorted byLow, we can confirm that we will not find more matches
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if inBetweenInclusive(value, intvl.Low, intvl.High) {
|
if inBetweenInclusive(value, low, high) {
|
||||||
matches = append(matches, intvl)
|
matches = append(matches, intvl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
32
gaps.go
32
gaps.go
@ -16,19 +16,33 @@ func (intvls *intervals) Gaps() []*Interval {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (intvls *intervals) calculateGaps() []*Interval {
|
func (intvls *intervals) calculateGaps() []*Interval {
|
||||||
|
list := []*Interval{}
|
||||||
|
if len(intvls.Intervals) == 0 {
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort intervals (if necessary)
|
||||||
intvls.Sort()
|
intvls.Sort()
|
||||||
gaps := []*Interval{}
|
|
||||||
lastMaxHigh := intvls.MinLow
|
gapThreshold := intvls.MinLow
|
||||||
for _, intvl := range intvls.Intervals {
|
for _, intvl := range intvls.Intervals {
|
||||||
if intvl.Low > lastMaxHigh {
|
// convert if necessary exclusive low/high values into inclusive ones
|
||||||
gaps = append(gaps, &Interval{Low: lastMaxHigh, High: intvl.Low - 1})
|
low, high := intvls.getInclusives(intvl.Low, intvl.High)
|
||||||
|
|
||||||
|
// if the current Low is higher than the last maximal High, means that there is a gap so we add this gap to the list
|
||||||
|
if low > gapThreshold {
|
||||||
|
list = append(list, &Interval{Low: gapThreshold, High: low - 1})
|
||||||
}
|
}
|
||||||
if intvl.High >= lastMaxHigh {
|
|
||||||
lastMaxHigh = intvl.High + 1
|
// update if necessary the threshold for the next gap
|
||||||
|
if high >= gapThreshold {
|
||||||
|
gapThreshold = high + 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if lastMaxHigh < intvls.MaxHigh {
|
|
||||||
gaps = append(gaps, &Interval{Low: lastMaxHigh, High: intvls.MaxHigh})
|
// if intvls.Intervals haven't covered all the range until the end, we need to fill the rest until the end as a gap
|
||||||
|
if gapThreshold < intvls.MaxHigh {
|
||||||
|
list = append(list, &Interval{Low: gapThreshold, High: intvls.MaxHigh})
|
||||||
}
|
}
|
||||||
return gaps
|
return list
|
||||||
}
|
}
|
||||||
|
3
get.go
3
get.go
@ -1,6 +1,9 @@
|
|||||||
package interval
|
package interval
|
||||||
|
|
||||||
func (intvls *intervals) Get() []*Interval {
|
func (intvls *intervals) Get() []*Interval {
|
||||||
|
// sort intervals (if necessary)
|
||||||
intvls.Sort()
|
intvls.Sort()
|
||||||
|
|
||||||
|
// return the intervals sorted
|
||||||
return intvls.Intervals
|
return intvls.Intervals
|
||||||
}
|
}
|
||||||
|
37
inclusives.go
Normal file
37
inclusives.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package interval
|
||||||
|
|
||||||
|
func (intvls *intervals) getInclusives(intervalLow, intervalHigh int) (int, int) {
|
||||||
|
low := intvls.getInclusiveLow(intervalLow)
|
||||||
|
high := intvls.getInclusiveHigh(intervalHigh)
|
||||||
|
if high < low {
|
||||||
|
high = low
|
||||||
|
}
|
||||||
|
if low > high {
|
||||||
|
low = high
|
||||||
|
}
|
||||||
|
return low, high
|
||||||
|
}
|
||||||
|
|
||||||
|
func (intvls *intervals) getInclusiveLow(value int) int {
|
||||||
|
if intvls.LowInclusive {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
// here the low is exclusive, we have to take the next value
|
||||||
|
newLow := value + 1
|
||||||
|
if newLow > intvls.MaxHigh {
|
||||||
|
return intvls.MaxHigh
|
||||||
|
}
|
||||||
|
return newLow
|
||||||
|
}
|
||||||
|
|
||||||
|
func (intvls *intervals) getInclusiveHigh(value int) int {
|
||||||
|
if intvls.HighInclusive {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
// here the high is exclusive, we have to take the previous value
|
||||||
|
newHigh := value - 1
|
||||||
|
if newHigh < intvls.MinLow {
|
||||||
|
return intvls.MinLow
|
||||||
|
}
|
||||||
|
return newHigh
|
||||||
|
}
|
36
intervals.go
36
intervals.go
@ -5,8 +5,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultMinLow = 0
|
defaultMinLow = 0
|
||||||
defaultMaxHigh = math.MaxInt64
|
defaultMaxHigh = math.MaxInt64
|
||||||
|
defaultLowInclusive = true
|
||||||
|
defaultHighInclusive = true
|
||||||
)
|
)
|
||||||
|
|
||||||
// Intervals is an interface to handle Interval structures discovering the existence of gaps or overlays
|
// Intervals is an interface to handle Interval structures discovering the existence of gaps or overlays
|
||||||
@ -39,6 +41,12 @@ type Intervals interface {
|
|||||||
|
|
||||||
// Report first sorts (if necessary) and then creates a report of the interval sequence
|
// Report first sorts (if necessary) and then creates a report of the interval sequence
|
||||||
Report() string
|
Report() string
|
||||||
|
|
||||||
|
// IsLowInclusive indicates if the Low part of the interval is included, e. g. (3,5) --> the 3 is included as part of the interval
|
||||||
|
IsLowInclusive() bool
|
||||||
|
|
||||||
|
// IsHighInclusive indicates if the High part of the interval is included, e. g. (3,5) --> the 5 is included as part of the interval
|
||||||
|
IsHighInclusive() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// intervals implements Intervals interface
|
// intervals implements Intervals interface
|
||||||
@ -50,19 +58,31 @@ type intervals struct {
|
|||||||
MinLow int
|
MinLow int
|
||||||
MaxHigh int
|
MaxHigh int
|
||||||
Sorted bool
|
Sorted bool
|
||||||
|
LowInclusive bool
|
||||||
|
HighInclusive bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewIntervalsDefault is a constructor that returns an instance of the Intervals interface with default values
|
// NewIntervalsDefault is a constructor that returns an instance of the Intervals interface with default values
|
||||||
func NewIntervalsDefault() Intervals {
|
func NewIntervalsDefault() Intervals {
|
||||||
return NewIntervals(defaultMinLow, defaultMaxHigh)
|
return NewIntervals(defaultMinLow, defaultMaxHigh, defaultLowInclusive, defaultHighInclusive)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewIntervals is a constructor that returns an instance of the Intervals interface
|
// NewIntervals is a constructor that returns an instance of the Intervals interface
|
||||||
func NewIntervals(minLow int, maxHigh int) Intervals {
|
func NewIntervals(minLow int, maxHigh int, lowInclusive bool, highInclusive bool) Intervals {
|
||||||
return &intervals{
|
return &intervals{
|
||||||
MinLow: minLow,
|
MinLow: minLow,
|
||||||
MaxHigh: maxHigh,
|
MaxHigh: maxHigh,
|
||||||
Intervals: []*Interval{},
|
Intervals: []*Interval{},
|
||||||
Sorted: false,
|
Sorted: false,
|
||||||
|
LowInclusive: lowInclusive,
|
||||||
|
HighInclusive: highInclusive,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (intvls *intervals) IsLowInclusive() bool {
|
||||||
|
return intvls.LowInclusive
|
||||||
|
}
|
||||||
|
|
||||||
|
func (intvls *intervals) IsHighInclusive() bool {
|
||||||
|
return intvls.HighInclusive
|
||||||
|
}
|
||||||
|
61
merge.go
61
merge.go
@ -8,33 +8,66 @@ func (intvls *intervals) Merge() []*Interval {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (intvls *intervals) calculateMerged() []*Interval {
|
func (intvls *intervals) calculateMerged() []*Interval {
|
||||||
intvls.Sort()
|
|
||||||
list := []*Interval{}
|
list := []*Interval{}
|
||||||
if len(intvls.Intervals) == 0 {
|
if len(intvls.Intervals) == 0 {
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
var lastLow int
|
// sort intervals (if necessary)
|
||||||
var lastHigh int
|
intvls.Sort()
|
||||||
|
|
||||||
|
var currentMinLow int
|
||||||
|
var currentMaxHigh int
|
||||||
for i, intvl := range intvls.Intervals {
|
for i, intvl := range intvls.Intervals {
|
||||||
|
// convert if necessary exclusive low/high values into inclusive ones
|
||||||
|
low, high := intvls.getInclusives(intvl.Low, intvl.High)
|
||||||
|
|
||||||
|
// in the first iteration it's for sure that don't exists merge, we just initialize variables
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
lastLow = intvl.Low
|
currentMinLow = low
|
||||||
lastHigh = intvl.High
|
currentMaxHigh = high
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if isLowInBetween(intvl.Low, intvl.High, lastLow, lastHigh) || isHighInBetween(intvl.Low, intvl.High, lastLow, lastHigh) {
|
|
||||||
if intvl.Low < lastLow {
|
// in case the current interval is overlapped or consecutive to the previous one, we need to update variables
|
||||||
lastLow = intvl.Low
|
// and keep going and process the next interval
|
||||||
|
if areSegmentsConsecutivesOrOverlapped(low, high, currentMinLow, currentMaxHigh) {
|
||||||
|
// update control variables if necessary
|
||||||
|
if low < currentMinLow {
|
||||||
|
currentMinLow = low
|
||||||
}
|
}
|
||||||
if intvl.High > lastHigh {
|
if high > currentMaxHigh {
|
||||||
lastHigh = intvl.High
|
currentMaxHigh = high
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
list = append(list, &Interval{Low: lastLow, High: lastHigh})
|
|
||||||
lastLow = intvl.Low
|
// here the segments are not consecutive or overlapped so we close this merged segment (add to the list)
|
||||||
lastHigh = intvl.High
|
list = append(list, &Interval{Low: currentMinLow, High: currentMaxHigh})
|
||||||
|
|
||||||
|
// update control variables
|
||||||
|
currentMinLow = low
|
||||||
|
currentMaxHigh = high
|
||||||
}
|
}
|
||||||
list = append(list, &Interval{Low: lastLow, High: lastHigh})
|
// the last segment is pending to be added to the list
|
||||||
|
list = append(list, &Interval{Low: currentMinLow, High: currentMaxHigh})
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func areSegmentsOverlapped(low, high, lastLow, lastHigh int) bool {
|
||||||
|
if isLowInBetweenInclusive(low, high, lastLow, lastHigh) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if isHighInBetweenInclusive(low, high, lastLow, lastHigh) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func areSegmentsConsecutives(low, high, lastLow, lastHigh int) bool {
|
||||||
|
return ((lastHigh + 1) == low) || ((high + 1) == lastLow)
|
||||||
|
}
|
||||||
|
|
||||||
|
func areSegmentsConsecutivesOrOverlapped(low, high, lastLow, lastHigh int) bool {
|
||||||
|
return areSegmentsOverlapped(low, high, lastLow, lastHigh) || areSegmentsConsecutives(low, high, lastLow, lastHigh)
|
||||||
|
}
|
||||||
|
35
overlap.go
35
overlap.go
@ -18,25 +18,38 @@ func (intvls *intervals) Overlapped() []*Interval {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (intvls *intervals) calculateOverlapped() []*Interval {
|
func (intvls *intervals) calculateOverlapped() []*Interval {
|
||||||
intvls.Sort()
|
|
||||||
list := []*Interval{}
|
list := []*Interval{}
|
||||||
|
if len(intvls.Intervals) == 0 {
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort intervals (if necessary)
|
||||||
|
intvls.Sort()
|
||||||
|
|
||||||
lastMinLow := math.MaxInt64
|
lastMinLow := math.MaxInt64
|
||||||
lastMaxHigh := math.MinInt64
|
lastMaxHigh := math.MinInt64
|
||||||
for i, intvl := range intvls.Intervals {
|
for i, intvl := range intvls.Intervals {
|
||||||
|
// convert if necessary exclusive low/high values into inclusive ones
|
||||||
|
low, high := intvls.getInclusives(intvl.Low, intvl.High)
|
||||||
|
|
||||||
|
// for the first iteration make no sense those operations
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
lowInBetween := isLowInBetween(lastMinLow, lastMaxHigh, intvl.Low, intvl.High)
|
// check if the front or back side of the current segment overlaps with the previous one
|
||||||
highInBetween := isHighInBetween(lastMinLow, lastMaxHigh, intvl.Low, intvl.High)
|
lowInBetween := isLowInBetweenInclusive(lastMinLow, lastMaxHigh, low, high)
|
||||||
|
highInBetween := isHighInBetweenInclusive(lastMinLow, lastMaxHigh, low, high)
|
||||||
if lowInBetween || highInBetween {
|
if lowInBetween || highInBetween {
|
||||||
greaterLow := max(intvl.Low, lastMinLow)
|
// extract which part is overlapped, create a new interval and add it to the list
|
||||||
lowerHigh := min(intvl.High, lastMaxHigh)
|
biggestLow := max(low, lastMinLow)
|
||||||
list = append(list, &Interval{Low: greaterLow, High: lowerHigh})
|
smallestHigh := min(high, lastMaxHigh)
|
||||||
|
list = append(list, &Interval{Low: biggestLow, High: smallestHigh})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if intvl.Low < lastMinLow {
|
// update control variables (if necessary)
|
||||||
lastMinLow = intvl.Low
|
if low < lastMinLow {
|
||||||
|
lastMinLow = low
|
||||||
}
|
}
|
||||||
if intvl.High > lastMaxHigh {
|
if high > lastMaxHigh {
|
||||||
lastMaxHigh = intvl.High
|
lastMaxHigh = high
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return list
|
return list
|
||||||
@ -44,7 +57,7 @@ func (intvls *intervals) calculateOverlapped() []*Interval {
|
|||||||
|
|
||||||
func (intvls *intervals) valueIsOverlapping(value int, overlapped []*Interval) bool {
|
func (intvls *intervals) valueIsOverlapping(value int, overlapped []*Interval) bool {
|
||||||
for _, ovrlp := range overlapped {
|
for _, ovrlp := range overlapped {
|
||||||
if inBetweenInclusive(value, ovrlp.Low, ovrlp.High) {
|
if inBetween(value, ovrlp.Low, ovrlp.High, intvls.LowInclusive, intvls.HighInclusive) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
1
sort.go
1
sort.go
@ -3,6 +3,7 @@ package interval
|
|||||||
import "sort"
|
import "sort"
|
||||||
|
|
||||||
func (intvls *intervals) Sort() {
|
func (intvls *intervals) Sort() {
|
||||||
|
// sort the intervals slice just if necessary
|
||||||
if !intvls.Sorted {
|
if !intvls.Sorted {
|
||||||
sort.Sort(ByLow(intvls.Intervals))
|
sort.Sort(ByLow(intvls.Intervals))
|
||||||
}
|
}
|
||||||
|
52
utils.go
52
utils.go
@ -13,6 +13,23 @@ func max(a, b int) int {
|
|||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func inBetween(i, min, max int, lowInclusive, highInclusive bool) bool {
|
||||||
|
if lowInclusive && highInclusive {
|
||||||
|
return inBetweenInclusive(i, min, max)
|
||||||
|
}
|
||||||
|
if !lowInclusive && !highInclusive {
|
||||||
|
return inBetweenExclusive(i, min, max)
|
||||||
|
}
|
||||||
|
if lowInclusive && !highInclusive {
|
||||||
|
return inBetweenLowInclusive(i, min, max)
|
||||||
|
}
|
||||||
|
if !lowInclusive && highInclusive {
|
||||||
|
return inBetweenHighInclusive(i, min, max)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// both inclusive
|
||||||
func inBetweenInclusive(i, min, max int) bool {
|
func inBetweenInclusive(i, min, max int) bool {
|
||||||
if (i >= min) && (i <= max) {
|
if (i >= min) && (i <= max) {
|
||||||
return true
|
return true
|
||||||
@ -20,6 +37,23 @@ func inBetweenInclusive(i, min, max int) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// only low is inclusive
|
||||||
|
func inBetweenLowInclusive(i, min, max int) bool {
|
||||||
|
if (i >= min) && (i < max) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// only high is inclusive
|
||||||
|
func inBetweenHighInclusive(i, min, max int) bool {
|
||||||
|
if (i > min) && (i <= max) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// both exclusive
|
||||||
func inBetweenExclusive(i, min, max int) bool {
|
func inBetweenExclusive(i, min, max int) bool {
|
||||||
if (i > min) && (i < max) {
|
if (i > min) && (i < max) {
|
||||||
return true
|
return true
|
||||||
@ -27,14 +61,28 @@ func inBetweenExclusive(i, min, max int) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func isLowInBetween(low1, high1, low2, high2 int) bool {
|
func isLowInBetween(low1, high1, low2, high2 int, lowInclusive, highInclusive bool) bool {
|
||||||
|
if inBetween(low1, low2, high2, lowInclusive, highInclusive) || inBetween(low2, low1, high1, lowInclusive, highInclusive) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func isLowInBetweenInclusive(low1, high1, low2, high2 int) bool {
|
||||||
if inBetweenInclusive(low1, low2, high2) || inBetweenInclusive(low2, low1, high1) {
|
if inBetweenInclusive(low1, low2, high2) || inBetweenInclusive(low2, low1, high1) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func isHighInBetween(low1, high1, low2, high2 int) bool {
|
func isHighInBetween(low1, high1, low2, high2 int, lowInclusive, highInclusive bool) bool {
|
||||||
|
if inBetween(high1, low2, high2, lowInclusive, highInclusive) || inBetween(high2, low1, high1, lowInclusive, highInclusive) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func isHighInBetweenInclusive(low1, high1, low2, high2 int) bool {
|
||||||
if inBetweenInclusive(high1, low2, high2) || inBetweenInclusive(high2, low1, high1) {
|
if inBetweenInclusive(high1, low2, high2) || inBetweenInclusive(high2, low1, high1) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user