first commit
This commit is contained in:
		
							
								
								
									
										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 | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user