first commit
This commit is contained in:
commit
21187ac4df
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# IDE
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
debug.test
|
20
interval.go
Normal file
20
interval.go
Normal file
@ -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 }
|
32
interval_test.go
Normal file
32
interval_test.go
Normal file
@ -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())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
252
intervals.go
Normal file
252
intervals.go
Normal file
@ -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 + "╣"
|
||||||
|
// }
|
28
utils.go
Normal file
28
utils.go
Normal file
@ -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
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user