From 21187ac4df9423ea3e27865b2934ee10b8d23e6c Mon Sep 17 00:00:00 2001 From: Daniel Gil Date: Thu, 24 May 2018 14:14:10 +0200 Subject: [PATCH] first commit --- .gitignore | 5 + interval.go | 20 ++++ interval_test.go | 32 ++++++ intervals.go | 252 +++++++++++++++++++++++++++++++++++++++++++++++ utils.go | 28 ++++++ 5 files changed, 337 insertions(+) create mode 100644 .gitignore create mode 100644 interval.go create mode 100644 interval_test.go create mode 100644 intervals.go create mode 100644 utils.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0906539 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# IDE +.idea/ +.vscode/ + +debug.test diff --git a/interval.go b/interval.go new file mode 100644 index 0000000..c226c42 --- /dev/null +++ b/interval.go @@ -0,0 +1,20 @@ +package interval + +import "fmt" + +type Interval struct { + Low int + High int + Object interface{} +} + +func (itvl Interval) String() string { + return fmt.Sprintf("(%v, %v) -> [%v]", itvl.Low, itvl.High, itvl.Object) +} + +// ByLow implements sort.Interface for []Interval based on the Low field. +type ByLow []*Interval + +func (itvls ByLow) Len() int { return len(itvls) } +func (itvls ByLow) Swap(i, j int) { itvls[i], itvls[j] = itvls[j], itvls[i] } +func (itvls ByLow) Less(i, j int) bool { return itvls[i].Low < itvls[j].Low } diff --git a/interval_test.go b/interval_test.go new file mode 100644 index 0000000..1266052 --- /dev/null +++ b/interval_test.go @@ -0,0 +1,32 @@ +package interval_test + +import ( + "testing" + + "bitbucket.org/differenttravel/interval" +) + +func TestInsert(t *testing.T) { + itvls := interval.NewIntervals(0, 100) + + itvls.Add(&interval.Interval{Low: 5, High: 7}) + itvls.Add(&interval.Interval{Low: 2, High: 4}) + itvls.Add(&interval.Interval{Low: 3, High: 6}) + itvls.Add(&interval.Interval{Low: 18, High: 20}) + itvls.Add(&interval.Interval{Low: 20, High: 30}) + itvls.Add(&interval.Interval{Low: 25, High: 28}) + itvls.Add(&interval.Interval{Low: 30, High: 32}) + + tt := []struct { + name string + itvls interval.Intervals + }{ + {name: "normal case", itvls: itvls}, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + t.Log(tc.itvls.Print()) + }) + } +} diff --git a/intervals.go b/intervals.go new file mode 100644 index 0000000..852fdd4 --- /dev/null +++ b/intervals.go @@ -0,0 +1,252 @@ +package interval + +import ( + "fmt" + "math" + "sort" +) + +const ( + defaultMinLow = 0 + defaultMaxHigh = math.MaxInt64 +) + +type Intervals interface { + Add(itvl *Interval) + Sort() + GetGaps() []*Interval + GetOverlapped() []*Interval + Print() string +} +type intervals struct { + Intervals []*Interval + MinLow int + MaxHigh int + Sorted bool +} + +func NewIntervalsDefault() Intervals { + return NewIntervals(defaultMinLow, defaultMaxHigh) +} + +func NewIntervals(minLow int, maxHigh int) Intervals { + return &intervals{ + MinLow: minLow, + MaxHigh: maxHigh, + Intervals: []*Interval{}, + Sorted: false, + } +} + +func (intvls *intervals) Add(itvl *Interval) { + intvls.Intervals = append(intvls.Intervals, itvl) + intvls.Sorted = false +} + +func (intvls *intervals) Sort() { + if !intvls.Sorted { + sort.Sort(ByLow(intvls.Intervals)) + } + intvls.Sorted = true +} + +func (intvls *intervals) GetGaps() []*Interval { + intvls.Sort() + gaps := []*Interval{} + lastHigh := intvls.MinLow + for _, intvl := range intvls.Intervals { + if intvl.Low > lastHigh { + gaps = append(gaps, &Interval{Low: lastHigh, High: intvl.Low - 1}) + } + lastHigh = intvl.High + 1 + } + if lastHigh < intvls.MaxHigh { + gaps = append(gaps, &Interval{Low: lastHigh, High: intvls.MaxHigh}) + } + return gaps +} + +func (intvls *intervals) GetOverlapped() []*Interval { + intvls.Sort() + list := []*Interval{} + lastMinLow := math.MaxInt64 + lastMaxHigh := math.MinInt64 + for i, intvl := range intvls.Intervals { + if i > 0 { + lowInBetween := inBetweenInclusive(lastMinLow, intvl.Low, intvl.High) || inBetweenInclusive(intvl.Low, lastMinLow, lastMaxHigh) + highInBetween := inBetweenInclusive(lastMaxHigh, intvl.Low, intvl.High) || inBetweenInclusive(intvl.High, lastMinLow, lastMaxHigh) + if lowInBetween || highInBetween { + greaterLow := max(intvl.Low, lastMinLow) + lowerHigh := min(intvl.High, lastMaxHigh) + list = append(list, &Interval{Low: greaterLow, High: lowerHigh}) + } + } + if intvl.Low < lastMinLow { + lastMinLow = intvl.Low + } + if intvl.High > lastMaxHigh { + lastMaxHigh = intvl.High + } + } + return list +} + +func (intvls *intervals) isAnOverlap(value int, overlapped []*Interval) bool { + for _, ovrlp := range overlapped { + if inBetweenInclusive(value, ovrlp.Low, ovrlp.High) { + return true + } + } + return false +} + +func (intvls *intervals) Print() string { + intvls.Sort() + // Available Symbols: ( ◯ ◌ ◍ ◎ ● ◉ ) , ( □ ■ ), ( ░ ▒ ▓ █ ) + emptySymbol := "◌" + fullSymbol := "◎" + overlapSymbol := "●" + separator := "║" + introText := fmt.Sprintf("\n==================================\n SUMMARY (minLow=%d, maxHigh=%d)\n==================================", intvls.MinLow, intvls.MaxHigh) + legend := fmt.Sprintf("\n • Legend: %v (empty), %v (full), %v (overlap)", emptySymbol, fullSymbol, overlapSymbol) + intervalText := "\n • Intervals: " + gapsText := "\n • Gaps: " + overlapText := "\n • Overlapped: " + graph := "" + index := intvls.MinLow + blockSize := 10 + numSeparators := 0 + + overlapped := intvls.GetOverlapped() + for i, ovrlp := range overlapped { + if i != 0 { + overlapText += ", " + } + overlapText += fmt.Sprintf("[%d,%d]", ovrlp.Low, ovrlp.High) + } + + for i, intvl := range intvls.Intervals { + if i != 0 { + intervalText += ", " + } + intervalText += fmt.Sprintf("[%d,%d]", intvl.Low, intvl.High) + for i := index; i < intvl.Low; i++ { + index++ + graph += emptySymbol + if index%10 == 0 { + graph += separator + numSeparators++ + } + } + + for i := index; i <= intvl.High; i++ { + if intvls.isAnOverlap(index, overlapped) { + graph += overlapSymbol + } else { + graph += fullSymbol + } + index++ + if index%blockSize == 0 { + graph += separator + numSeparators++ + } + } + } + gaps := intvls.GetGaps() + for i, gap := range gaps { + if i != 0 { + gapsText += ", " + } + gapsText += fmt.Sprintf("[%d,%d]", gap.Low, gap.High) + } + + for i := index; i < intvls.MaxHigh; i++ { + graph += emptySymbol + } + axisLegend := fmt.Sprintf(" %v", intvls.MinLow) + for i := intvls.MinLow; i < intvls.MaxHigh+numSeparators-2; i++ { + axisLegend += " " + } + axisLegend += fmt.Sprintf("%v", intvls.MaxHigh) + graphText := fmt.Sprintf("\n\n%s\n╠%s╣", axisLegend, graph) + return introText + legend + intervalText + gapsText + overlapText + graphText +} + +// func (i *Intervals) Insert(intvl *Interval) { +// i.Intervals = append(i.Intervals, intvl) + +// if i.Root == nil { +// // create the root node +// i.Root = &Node{Value: intvl, Left: nil, Right: nil} +// } else { +// currentNode := i.Root + +// // search for the next block or the last +// for { +// next := currentNode.Next() +// if next == nil { +// // append the new interval to the right of the last element +// if intvl.Low >= currentNode.Value.Low { +// currentNode.Right = &Node{Value: intvl, Left: currentNode, Right: nil} +// } else { +// newNode := &Node{Value: intvl, Left: currentNode.Left, Right: currentNode} +// currentNode.Left = newNode +// } +// return +// } else if currentNode.OverlapsWith(intvl) { +// if intvl.Low >= next.Value.Low { +// // insert the new interval to the right of the current node +// newNode := &Node{Value: intvl, Left: currentNode, Right: currentNode.Right} +// currentNode.Right = newNode +// } else { +// // insert the new interval to the left of the current node +// newNode := &Node{Value: intvl, Left: currentNode.Left, Right: currentNode} +// currentNode.Left = newNode +// } +// } +// } +// } +// } + +// func (i *Intervals) appendInterval(intvl *Interval, n *Node) { +// if intvl.Low >= n.Value.Low { +// // insert the new interval to the right of the current node +// newNode := &Node{Value: intvl, Left: currentNode, Right: currentNode.Right} +// currentNode.Right = newNode +// } else { +// // insert the new interval to the left of the current node +// newNode := &Node{Value: intvl, Left: currentNode.Left, Right: currentNode} +// currentNode.Left = newNode +// } +// } + +// func (i *intervals) Print() string { +// emptySymbol := "░" +// fullSymbol := "█" // '▒' '▓' +// separator := "║" +// text := "" +// graph := "╠" +// index := 0 +// for i, intvl := range i.Intervals { +// if i != 0 { +// text += ", " +// } +// text += fmt.Sprintf("[%d,%d]", intvl.Low, intvl.High) +// for i := index; i < intvl.Low; i++ { +// index++ +// graph += emptySymbol +// if index%10 == 0 { +// graph += separator +// } +// } + +// for i := index; i < intvl.High; i++ { +// index++ +// graph += fullSymbol +// if index%10 == 0 { +// graph += separator +// } +// } +// } +// return text + "\n" + graph + "╣" +// } diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..2248783 --- /dev/null +++ b/utils.go @@ -0,0 +1,28 @@ +package interval + +func min(a, b int) int { + if a < b { + return a + } + return b +} +func max(a, b int) int { + if a > b { + return a + } + return b +} + +func inBetweenInclusive(i, min, max int) bool { + if (i >= min) && (i <= max) { + return true + } + return false +} + +func inBetweenExclusive(i, min, max int) bool { + if (i > min) && (i < max) { + return true + } + return false +}