From 786bcb162b2556f638b1f0279e45464a9f219940 Mon Sep 17 00:00:00 2001 From: Daniel Gil Date: Fri, 25 May 2018 13:02:52 +0200 Subject: [PATCH] splited big file into smaller files by functionality --- add.go | 13 ++++ find.go | 16 ++++ gaps.go | 34 +++++++++ interval.go | 1 + interval_test.go | 2 +- intervals.go | 192 +++++------------------------------------------ merge.go | 32 ++++++++ overlap.go | 52 +++++++++++++ report.go | 131 ++++++++++++++++++++++++++++++++ sort.go | 10 +++ 10 files changed, 308 insertions(+), 175 deletions(-) create mode 100644 add.go create mode 100644 find.go create mode 100644 gaps.go create mode 100644 merge.go create mode 100644 overlap.go create mode 100644 report.go create mode 100644 sort.go diff --git a/add.go b/add.go new file mode 100644 index 0000000..701a496 --- /dev/null +++ b/add.go @@ -0,0 +1,13 @@ +package interval + +func (intvls *intervals) Add(itvl *Interval) { + intvls.Intervals = append(intvls.Intervals, itvl) + intvls.reset() +} + +func (intvls *intervals) reset() { + intvls.Sorted = false + intvls.GapsList = nil + intvls.OverlappedList = nil + intvls.MergeList = nil +} diff --git a/find.go b/find.go new file mode 100644 index 0000000..52b25d7 --- /dev/null +++ b/find.go @@ -0,0 +1,16 @@ +package interval + +func (intvls *intervals) FindIntervalsForValue(value int) []*Interval { + intvls.Sort() + var matches []*Interval + for _, intvl := range intvls.Intervals { + if intvl.Low > value { + // due to the intervals are sorted, we can confirm that we will not find more matches + break + } + if inBetweenInclusive(value, intvl.Low, intvl.High) { + matches = append(matches, intvl) + } + } + return matches +} diff --git a/gaps.go b/gaps.go new file mode 100644 index 0000000..19ffecc --- /dev/null +++ b/gaps.go @@ -0,0 +1,34 @@ +package interval + +func (intvls *intervals) HasGaps() bool { + intvls.Gaps() + if intvls.GapsList != nil && len(intvls.GapsList) > 0 { + return true + } + return false +} + +func (intvls *intervals) Gaps() []*Interval { + if intvls.GapsList == nil { + intvls.GapsList = intvls.calculateGaps() + } + return intvls.GapsList +} + +func (intvls *intervals) calculateGaps() []*Interval { + intvls.Sort() + gaps := []*Interval{} + lastMaxHigh := intvls.MinLow + for _, intvl := range intvls.Intervals { + if intvl.Low > lastMaxHigh { + gaps = append(gaps, &Interval{Low: lastMaxHigh, High: intvl.Low - 1}) + } + if intvl.High >= lastMaxHigh { + lastMaxHigh = intvl.High + 1 + } + } + if lastMaxHigh < intvls.MaxHigh { + gaps = append(gaps, &Interval{Low: lastMaxHigh, High: intvls.MaxHigh}) + } + return gaps +} diff --git a/interval.go b/interval.go index 640d460..778c008 100644 --- a/interval.go +++ b/interval.go @@ -8,6 +8,7 @@ type Interval struct { Object interface{} } +// String implements Stringer.Interface Interval func (itvl Interval) String() string { return fmt.Sprintf("(%v, %v)", itvl.Low, itvl.High) } diff --git a/interval_test.go b/interval_test.go index 938c3f2..01b7de8 100644 --- a/interval_test.go +++ b/interval_test.go @@ -27,7 +27,7 @@ func TestInsert(t *testing.T) { for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { - t.Log(tc.itvls.Print()) + t.Log(tc.itvls.Report()) }) } } diff --git a/intervals.go b/intervals.go index 632b5ce..c4517ac 100644 --- a/intervals.go +++ b/intervals.go @@ -1,9 +1,7 @@ package interval import ( - "fmt" "math" - "sort" ) const ( @@ -18,25 +16,37 @@ type Intervals interface { // Sort sorts the intervals list by the Low property (ascending) Sort() + // HasGaps returns true if exists gaps for the introduced intervals between MinLow and MaxHigh + HasGaps() bool + // Gaps first sorts (if necessary) and then returns the interval gaps Gaps() []*Interval + // Merge first sorts (if necessary) and then fussion together overlapped intervals + Merge() []*Interval + + // HasOverlapped returns true if exists overlapping for the introduced intervals + HasOverlapped() bool + // Overlapped first sorts (if necessary) and then returns the overlapped intervals Overlapped() []*Interval // FindIntervalsForValue returns all the intervals which contains the passed value FindIntervalsForValue(value int) []*Interval - // Print first sorts (if necessary) and then displays graphically the interval sequence - Print() string + // Report first sorts (if necessary) and then creates a report of the interval sequence + Report() string } // intervals implements Intervals interface type intervals struct { - Intervals []*Interval - MinLow int - MaxHigh int - Sorted bool + Intervals []*Interval + GapsList []*Interval + OverlappedList []*Interval + MergeList []*Interval + MinLow int + MaxHigh int + Sorted bool } // NewIntervalsDefault is a constructor that returns an instance of the Intervals interface with default values @@ -53,169 +63,3 @@ func NewIntervals(minLow int, maxHigh int) Intervals { Sorted: false, } } - -func (intvls *intervals) Add(itvl *Interval) { - intvls.Intervals = append(intvls.Intervals, itvl) - intvls.Sorted = false -} - -func (intvls *intervals) FindIntervalsForValue(value int) []*Interval { - intvls.Sort() - var matches []*Interval - for _, intvl := range intvls.Intervals { - if intvl.Low > value { - // due to the intervals are sorted, we can confirm that we will not find more matches - break - } - if inBetweenInclusive(value, intvl.Low, intvl.High) { - matches = append(matches, intvl) - } - } - return matches -} - -func (intvls *intervals) Sort() { - if !intvls.Sorted { - sort.Sort(ByLow(intvls.Intervals)) - } - intvls.Sorted = true -} - -func (intvls *intervals) Gaps() []*Interval { - intvls.Sort() - gaps := []*Interval{} - lastMaxHigh := intvls.MinLow - for _, intvl := range intvls.Intervals { - if intvl.Low > lastMaxHigh { - gaps = append(gaps, &Interval{Low: lastMaxHigh, High: intvl.Low - 1}) - } - // lastHigh = intvl.High + 1 - if intvl.High >= lastMaxHigh { - lastMaxHigh = intvl.High + 1 - } - } - if lastMaxHigh < intvls.MaxHigh { - gaps = append(gaps, &Interval{Low: lastMaxHigh, High: intvls.MaxHigh}) - } - return gaps -} - -func (intvls *intervals) Overlapped() []*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) isOverlapping(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 := "●" - leadingSymbol := "├" - trailingSymbol := "┤" - 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.Overlapped() - for i, ovrlp := range overlapped { - if i != 0 { - overlapText += ", " - } - overlapText += fmt.Sprintf("[%d,%d]", ovrlp.Low, ovrlp.High) - } - - for j, intvl := range intvls.Intervals { - if j != 0 { - intervalText += ", " - } - intervalText += fmt.Sprintf("[%d,%d]", intvl.Low, intvl.High) - for i := index; i < intvl.Low; i++ { - index++ - graph += emptySymbol - if index%blockSize == 0 { - graph += separator - numSeparators++ - } - } - - for i := index; i <= intvl.High; i++ { - if intvls.isOverlapping(index, overlapped) { - graph += overlapSymbol - } else { - graph += fullSymbol - } - index++ - if index%blockSize == 0 { - graph += separator - numSeparators++ - } - } - } - gaps := intvls.Gaps() - for i, gap := range gaps { - if i != 0 { - gapsText += ", " - } - gapsText += fmt.Sprintf("[%d,%d]", gap.Low, gap.High) - } - - for i := index + 1; i < intvls.MaxHigh; i++ { - graph += emptySymbol - if i%blockSize == 0 { - graph += separator - numSeparators++ - } - } - axisLegend := " " - numSeparators = 0 - for i := intvls.MinLow; i < intvls.MaxHigh/blockSize; i++ { - mark := fmt.Sprintf("%d", i*blockSize) - axisLegend += mark - limit := (blockSize - len(mark)) + 1 - for j := 0; j < limit; j++ { - axisLegend += " " - } - } - axisLegend += fmt.Sprintf("%v", intvls.MaxHigh) - graphText := fmt.Sprintf("\n\n%s\n%s%s%s", axisLegend, leadingSymbol, graph, trailingSymbol) - return "\n" + introText + legend + intervalText + gapsText + overlapText + graphText + "\n" -} diff --git a/merge.go b/merge.go new file mode 100644 index 0000000..8074c11 --- /dev/null +++ b/merge.go @@ -0,0 +1,32 @@ +package interval + +func (intvls *intervals) Merge() []*Interval { + if intvls.MergeList == nil { + intvls.MergeList = intvls.calculateMerged() + } + return intvls.MergeList +} + +func (intvls *intervals) calculateMerged() []*Interval { + intvls.Sort() + list := []*Interval{} + var lastLow int + var lastHigh int + for i, intvl := range intvls.Intervals { + if i == 0 { + lastLow = intvl.Low + lastHigh = intvl.High + continue + } + if inBetweenInclusive(intvl.Low, lastLow, lastHigh) { + // because the intervals are previously sorted, we just need to take care of the High value + if intvl.High > lastHigh { + lastHigh = intvl.High + } + continue + } + list = append(list, &Interval{Low: lastLow, High: lastHigh}) + } + list = append(list, &Interval{Low: lastLow, High: lastHigh}) + return list +} diff --git a/overlap.go b/overlap.go new file mode 100644 index 0000000..2401536 --- /dev/null +++ b/overlap.go @@ -0,0 +1,52 @@ +package interval + +import "math" + +func (intvls *intervals) HasOverlapped() bool { + intvls.Overlapped() + if intvls.OverlappedList != nil && len(intvls.OverlappedList) > 0 { + return true + } + return false +} + +func (intvls *intervals) Overlapped() []*Interval { + if intvls.OverlappedList == nil { + intvls.OverlappedList = intvls.calculateOverlapped() + } + return intvls.OverlappedList +} + +func (intvls *intervals) calculateOverlapped() []*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) isOverlapping(value int, overlapped []*Interval) bool { + for _, ovrlp := range overlapped { + if inBetweenInclusive(value, ovrlp.Low, ovrlp.High) { + return true + } + } + return false +} diff --git a/report.go b/report.go new file mode 100644 index 0000000..74b3a57 --- /dev/null +++ b/report.go @@ -0,0 +1,131 @@ +package interval + +import "fmt" + +var blockSize = 10 + +type symbols struct { + emptySymbol string + fullSymbol string + overlapSymbol string + leadingSymbol string + trailingSymbol string + separator string +} + +func newSymbols() symbols { + // Available Symbols: ( ◯ ◌ ◍ ◎ ● ◉ ) , ( □ ■ ), ( ░ ▒ ▓ █ ) + return symbols{ + emptySymbol: "◌", + fullSymbol: "◎", + overlapSymbol: "●", + leadingSymbol: "├", + trailingSymbol: "┤", + separator: "|", + } +} + +func (intvls *intervals) Report() string { + intvls.Sort() + symbols := newSymbols() + intro := intvls.buildHeading(symbols) + index := intvls.MinLow + numSeparators := 0 + overlapText := intvls.reportOverlapped() + intervalText := "" + graph := "" + intervalText, index, graph, numSeparators = intvls.reportIntervals(index, symbols, numSeparators) + gapsText := intvls.reportGaps() + graph, numSeparators, index = intvls.fillUntilTheEnd(index, symbols, graph, numSeparators) + axisLegend := intvls.buildAxisLegend() + graphText := fmt.Sprintf("\n\n%s\n%s%s%s", axisLegend, symbols.leadingSymbol, graph, symbols.trailingSymbol) + return "\n" + intro + intervalText + gapsText + overlapText + graphText + "\n" +} + +func (intvls *intervals) buildHeading(symbols symbols) string { + introText := fmt.Sprintf("\n==================================\n REPORT (minLow=%d, maxHigh=%d)\n==================================", intvls.MinLow, intvls.MaxHigh) + legend := fmt.Sprintf("\n • Legend: %v (empty), %v (full), %v (overlap)", symbols.emptySymbol, symbols.fullSymbol, symbols.overlapSymbol) + return introText + legend +} + +func (intvls *intervals) reportIntervals(index int, symbols symbols, numSeparators int) (string, int, string, int) { + graph := "" + intervalText := "\n • Intervals: " + for j, intvl := range intvls.Intervals { + if j != 0 { + intervalText += ", " + } + intervalText += fmt.Sprintf("[%d,%d]", intvl.Low, intvl.High) + for i := index; i < intvl.Low; i++ { + index++ + graph += symbols.emptySymbol + if index%blockSize == 0 { + graph += symbols.separator + numSeparators++ + } + } + + for i := index; i <= intvl.High; i++ { + if intvls.isOverlapping(index, intvls.OverlappedList) { + graph += symbols.overlapSymbol + } else { + graph += symbols.fullSymbol + } + index++ + if index%blockSize == 0 { + graph += symbols.separator + numSeparators++ + } + } + } + return intervalText, index, graph, numSeparators +} + +func (intvls *intervals) fillUntilTheEnd(index int, symbols symbols, graph string, numSeparators int) (string, int, int) { + for i := index + 1; i <= intvls.MaxHigh+1; i++ { + graph += symbols.emptySymbol + if i%blockSize == 0 { + graph += symbols.separator + numSeparators++ + } + } + return graph, numSeparators, index +} + +func (intvls *intervals) reportOverlapped() string { + overlapText := "\n • Overlapped: " + overlapped := intvls.Overlapped() + for i, ovrlp := range overlapped { + if i != 0 { + overlapText += ", " + } + overlapText += fmt.Sprintf("[%d,%d]", ovrlp.Low, ovrlp.High) + } + return overlapText +} + +func (intvls *intervals) reportGaps() string { + gapsText := "\n • Gaps: " + gaps := intvls.Gaps() + for i, gap := range gaps { + if i != 0 { + gapsText += ", " + } + gapsText += fmt.Sprintf("[%d,%d]", gap.Low, gap.High) + } + return gapsText +} + +func (intvls *intervals) buildAxisLegend() string { + axisLegend := " " + for i := intvls.MinLow; i < intvls.MaxHigh/blockSize; i++ { + mark := fmt.Sprintf("%d", i*blockSize) + axisLegend += mark + limit := (blockSize - len(mark)) + 1 + for j := 0; j < limit; j++ { + axisLegend += " " + } + } + axisLegend += fmt.Sprintf("%v", intvls.MaxHigh) + return axisLegend +} diff --git a/sort.go b/sort.go new file mode 100644 index 0000000..19a77a3 --- /dev/null +++ b/sort.go @@ -0,0 +1,10 @@ +package interval + +import "sort" + +func (intvls *intervals) Sort() { + if !intvls.Sorted { + sort.Sort(ByLow(intvls.Intervals)) + } + intvls.Sorted = true +}