Skip to content

Commit

Permalink
feat: create diff functionnality v2
Browse files Browse the repository at this point in the history
  • Loading branch information
Shinelia committed Jan 11, 2023
1 parent ff5587a commit 0449668
Show file tree
Hide file tree
Showing 8 changed files with 236 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
vendor
.devcontainer
.DS_Store
15 changes: 15 additions & 0 deletions v2/date_time_range.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,18 @@ func formatNumberRangeToDateRange(numberRange NumberRange[int64]) DateTimeRange
}
return dateTimeRange
}

func (r DateTimeRange) Difference(r1 DateTimeRange) []DateTimeRange {
var differenceResult = diff(
r.formatToNumberRange(),
r1.formatToNumberRange(),
)
if len(differenceResult) == 0 {
return nil
}
diffDateTimeRange := make([]DateTimeRange, 0)
for _, rangeNumber := range differenceResult {
diffDateTimeRange = append(diffDateTimeRange, formatNumberRangeToDateRange(rangeNumber))
}
return diffDateTimeRange
}
72 changes: 72 additions & 0 deletions v2/date_time_range_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,75 @@ func TestExclusiveIntersectionForTimeRange(t *testing.T) {
assert.True(t, expectedIntersectionRange[1].IsExclusive)
})
}

func TestDifferenceForTimeRange(t *testing.T) {
t.Run("expect difference between same ranges with one of the bounds is exclusive", func(t *testing.T) {
now := time.Now()
range1 := DateTimeRange{
{Value: now.Add(-10 * time.Hour).Truncate(time.Minute)},
{Value: now.Add(-5 * time.Hour).Truncate(time.Minute), IsExclusive: true},
}
range2 := DateTimeRange{
{Value: now.Add(-10 * time.Hour).Truncate(time.Minute)},
{Value: now.Add(-5 * time.Hour).Truncate(time.Minute)},
}
diffRange := range1.Difference(range2)
assert.Equal(t, 1, len(diffRange))
assert.True(t, diffRange[0][0].Value.Equal(range2[1].Value))
assert.True(t, diffRange[0][1].Value.Equal(range2[1].Value))
})

t.Run("expect difference with second range includes in first range", func(t *testing.T) {
// 1. --------------
// 2. ------
range1 := DateTimeRange{
{Value: time.Now().Add(-10 * time.Hour).Truncate(time.Minute)},
{Value: time.Now().Add(-5 * time.Hour).Truncate(time.Minute)},
}
range2 := DateTimeRange{
{Value: time.Now().Add(-7 * time.Hour).Truncate(time.Minute)},
{Value: time.Now().Add(-6 * time.Hour).Truncate(time.Minute)},
}
diffRange := range1.Difference(range2)
expectedDifferenceRange := []DateTimeRange{
{range1[0], range2[0]},
{range2[1], range1[1]},
}
assert.Equal(t, len(diffRange), len(expectedDifferenceRange))
assert.True(t, diffRange[0][0].Value.Equal(expectedDifferenceRange[0][0].Value))
assert.True(t, diffRange[0][1].Value.Equal(expectedDifferenceRange[0][1].Value))
assert.True(t, diffRange[1][0].Value.Equal(expectedDifferenceRange[1][0].Value))
assert.True(t, diffRange[1][1].Value.Equal(expectedDifferenceRange[1][1].Value))

diffRange = range2.Difference(range1)
assert.Equal(t, 0, len(diffRange))
})

t.Run("expect difference with 2 ranges in intersection", func(t *testing.T) {
// 1. --------------
// 2. ------
range1 := DateTimeRange{
{Value: time.Now().Add(-10 * time.Hour).Truncate(time.Minute)},
{Value: time.Now().Add(-6 * time.Hour).Truncate(time.Minute)},
}
range2 := DateTimeRange{
{Value: time.Now().Add(-7 * time.Hour).Truncate(time.Minute)},
{Value: time.Now().Add(-3 * time.Hour).Truncate(time.Minute)},
}
diffRange := range1.Difference(range2)
expectedDifferenceRange := []DateTimeRange{
{range1[0], range2[0]},
}
assert.Equal(t, len(diffRange), len(expectedDifferenceRange))
assert.True(t, diffRange[0][0].Value.Equal(expectedDifferenceRange[0][0].Value))
assert.True(t, diffRange[0][1].Value.Equal(expectedDifferenceRange[0][1].Value))

diffRange = range2.Difference(range1)
expectedDifferenceRange = []DateTimeRange{
{range1[1], range2[1]},
}
assert.Equal(t, len(diffRange), len(expectedDifferenceRange))
assert.Equal(t, diffRange[0][0].Value, expectedDifferenceRange[0][0].Value)
assert.Equal(t, diffRange[0][1].Value, expectedDifferenceRange[0][1].Value)
})
}
45 changes: 45 additions & 0 deletions v2/diff.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package grange

func diff[K Number, L NumberRange[K]](r L, r1 L) []NumberRange[K] {
// return substract dateRange2 into dateRange1
// if no intersection, the dateRange1 is returneed
start1 := r[0]
end1 := r[1]
start2 := r1[0]
end2 := r1[1]

if end1.Value < start2.Value || end2.Value < start1.Value {
return []NumberRange[K]{
{start1, end1},
}
}

ranges := make([]NumberRange[K], 0)
if start1.Value < start2.Value && start2.Value < end1.Value {
ranges = append(ranges, NumberRange[K]{start1, start2})
}

if start1.Value == start2.Value && (start2.IsExclusive != start1.IsExclusive) {
if !start1.IsExclusive {
ranges = append(ranges, NumberRange[K]{start1, start1})
} else {
ranges = append(ranges, NumberRange[K]{start2, start2})

}
}

if end1.Value == end2.Value && (end2.IsExclusive != end1.IsExclusive) {
if !end1.IsExclusive {
ranges = append(ranges, NumberRange[K]{end1, end1})
} else {
ranges = append(ranges, NumberRange[K]{end2, end2})

}
}

if end2.Value < end1.Value && end2.Value > start1.Value && end2.Value != end1.Value {
ranges = append(ranges, NumberRange[K]{end2, end1})
}

return ranges
}
4 changes: 4 additions & 0 deletions v2/number_range.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,7 @@ func formatEmptyExclusiveRange[K Number](r NumberRange[K]) *NumberRange[K] {
}
return &r
}

func (r NumberRange[K]) Difference(r1 NumberRange[K]) []NumberRange[K] {
return diff(r, r1)
}
78 changes: 78 additions & 0 deletions v2/number_range_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,81 @@ func TestExclusiveIntersectionForNumberRange(t *testing.T) {
assert.Nil(t, intersectionRange)
})
}

func TestDifferenceForNumberRange(t *testing.T) {
t.Run("expect difference between same ranges with one of the bounds is exclusive", func(t *testing.T) {
range1 := NumberRange[int]{
{Value: 0}, {Value: 20},
}
range2 := NumberRange[int]{
{Value: 0}, {Value: 20, IsExclusive: true},
}
diffRange := range1.Difference(range2)
expectedDifferenceRange := []NumberRange[int]{
{range1[1], range1[1]},
}
assert.Equal(t, len(diffRange), len(expectedDifferenceRange))
assert.Equal(t, diffRange[0][0].Value, expectedDifferenceRange[0][0].Value)
assert.Equal(t, diffRange[0][1].Value, expectedDifferenceRange[0][1].Value)
assert.False(t, diffRange[0][0].IsExclusive)
assert.False(t, diffRange[0][1].IsExclusive)

diffRange = range2.Difference(range1)
assert.Equal(t, len(diffRange), len(expectedDifferenceRange))
assert.Equal(t, diffRange[0][0].Value, expectedDifferenceRange[0][0].Value)
assert.Equal(t, diffRange[0][1].Value, expectedDifferenceRange[0][1].Value)
assert.False(t, diffRange[0][0].IsExclusive)
assert.False(t, diffRange[0][1].IsExclusive)
})

t.Run("expect difference with second range includes in first range", func(t *testing.T) {
// 1. --------------
// 2. ------
range1 := NumberRange[int]{
{Value: 0}, {Value: 20},
}
range2 := NumberRange[int]{
{Value: 10}, {Value: 15},
}
diffRange := range1.Difference(range2)
expectedDifferenceRange := []NumberRange[int]{
{range1[0], range2[0]},
{range2[1], range1[1]},
}
assert.Equal(t, len(diffRange), len(expectedDifferenceRange))
assert.Equal(t, diffRange[0][0].Value, expectedDifferenceRange[0][0].Value)
assert.Equal(t, diffRange[0][1].Value, expectedDifferenceRange[0][1].Value)
assert.Equal(t, diffRange[1][0].Value, expectedDifferenceRange[1][0].Value)
assert.Equal(t, diffRange[1][1].Value, expectedDifferenceRange[1][1].Value)

diffRange = range2.Difference(range1)
assert.Equal(t, 0, len(diffRange))
})

t.Run("expect difference with 2 ranges in intersection", func(t *testing.T) {
// 1. --------------
// 2. ------
range1 := NumberRange[int]{
{Value: 0}, {Value: 20},
}
range2 := NumberRange[int]{
{Value: 10}, {Value: 30},
}
diffRange := range1.Difference(range2)
expectedDifferenceRange := []NumberRange[int]{
{range1[0], range2[0]},
}
assert.Equal(t, len(diffRange), len(expectedDifferenceRange))
assert.Equal(t, diffRange[0][0].Value, expectedDifferenceRange[0][0].Value)
assert.Equal(t, diffRange[0][1].Value, expectedDifferenceRange[0][1].Value)

diffRange = range2.Difference(range1)
expectedDifferenceRange = []NumberRange[int]{
{range1[1], range2[1]},
}
assert.Equal(t, len(diffRange), len(expectedDifferenceRange))
assert.Equal(t, diffRange[0][0].Value, expectedDifferenceRange[0][0].Value)
assert.Equal(t, diffRange[0][1].Value, expectedDifferenceRange[0][1].Value)
})

}
5 changes: 5 additions & 0 deletions v2/range.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,15 @@ type RangeTypes interface {

type RangeInterface[K RangeTypes] interface {
Intersection(K) *K
Difference(K) []K
IsEmpty() bool
ToPostgresString() (string, string, string) // return lowerBound, upperBound, inclusivity/exclusivity of bounds for postgres
}

func Intersection[K RangeTypes](t RangeInterface[K], t1 K) *K {
return t.Intersection(t1)
}

func Difference[K RangeTypes](t RangeInterface[K], t1 K) []K {
return t.Difference(t1)
}
16 changes: 16 additions & 0 deletions v2/range_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,19 @@ func TestIntersectionForRangeInterfaceWithNumberType(t *testing.T) {
assert.Equal(t, expectedIntersectionRange[0].Value, intersectionRange[0].Value)
assert.Equal(t, expectedIntersectionRange[1].Value, intersectionRange[1].Value)
}

func TestDifferenceForRangeInterfaceWithNumberType(t *testing.T) {
range1 := NumberRange[int]{
{Value: 0}, {Value: 20},
}
range2 := NumberRange[int]{
{Value: -10}, {Value: 10},
}
differenceRange := Difference[NumberRange[int]](range1, range2)
expectedDifferenceRange := []NumberRange[int]{
{range2[1],
range1[1]},
}
assert.Equal(t, expectedDifferenceRange[0][0].Value, differenceRange[0][0].Value)
assert.Equal(t, expectedDifferenceRange[0][1].Value, differenceRange[0][1].Value)
}

0 comments on commit 0449668

Please sign in to comment.