Skip to content

Commit

Permalink
Add union and intersection for list type
Browse files Browse the repository at this point in the history
  • Loading branch information
Tinku10 committed Oct 6, 2024
1 parent fe0c17c commit 5317e1f
Show file tree
Hide file tree
Showing 2 changed files with 207 additions and 40 deletions.
175 changes: 135 additions & 40 deletions types/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,23 @@ package types
import (
"reflect"
"sort"
"strings"

"github.com/Jintumoni/vortex/errors"
)

type ListType struct {
Value []BaseType
elemType reflect.Type
Value []BaseType
}

func NewList(value []BaseType, elemType reflect.Type) (*ListType, error) {
func NewList(value []BaseType) (*ListType, error) {
// check if all the elements of the list are of same type
for _, e := range value {
if reflect.TypeOf(e) != elemType {
if reflect.TypeOf(e) != reflect.TypeOf(value[0]) {
return nil, errors.UnhomogeneousType
}
}
return &ListType{
Value: value,
elemType: elemType,
}, nil
return &ListType{Value: value}, nil
}

func (l *ListType) And(other Logical) (Logical, error) {
Expand All @@ -33,36 +30,13 @@ func (l *ListType) And(other Logical) (Logical, error) {
return nil, errors.InvalidOperation
}

// validate if both the list have same type of elements
if l.elemType != o.elemType {
// Validate if both the list have same type of elements
// A list is expected to be homogeneous, so validating only the first element works
if reflect.TypeOf(l.Value[0]) != reflect.TypeOf(o.Value[0]) {
return nil, errors.InvalidOperation
}

res, _ := NewList(nil, l.elemType)

// intersect the two lists
sort.Slice(l.Value, func(i, j int) bool {
switch l.Value[i].(type) {
case *IntType:
less, err := l.Value[i].(*IntType).LessThan(l.Value[j].(*IntType))
if err != nil {
return false
}
return less
case *BoolType:
return false
case *UserDefinedType:
less, err := l.Value[i].(*UserDefinedType).LessThan(l.Value[j].(*UserDefinedType))
if err != nil {
return false
}
return less
default:
return false
}
})

return res, nil
return intersect(l, o)
}

func (l *ListType) Or(other Logical) (Logical, error) {
Expand All @@ -73,14 +47,135 @@ func (l *ListType) Or(other Logical) (Logical, error) {
return nil, errors.InvalidOperation
}

// validate if both the list have same type of elements
if l.elemType != o.elemType {
// Validate if both the list have same type of elements
// A list is expected to be homogeneous, so validating only the first element works
if reflect.TypeOf(l.Value[0]) != reflect.TypeOf(o.Value[0]) {
return nil, errors.InvalidOperation
}

res, _ := NewList(nil, l.elemType)
return unite(l, o)
}

func (l *ListType) Repr() string {
var s strings.Builder
s.WriteString("[")
for i, e := range l.Value {
if i > 0 {
s.WriteString(", ")
}
s.WriteString(e.Repr())
}
s.WriteString("]")

return s.String()
}

func (l *ListType) validateType() error {
for _, e := range l.Value {
if reflect.TypeOf(e) != reflect.TypeOf(l.Value[0]) {
return errors.UnhomogeneousType
}
}
return nil
}

func intersect(a *ListType, b *ListType) (*ListType, error) {
// intersect the two lists
sort.Slice(a.Value, func(i, j int) bool {
less, err := a.Value[i].LessThan(a.Value[j])
if err != nil {
return false
}
return less
})

// unite the two lists
sort.Slice(b.Value, func(i, j int) bool {
less, err := b.Value[i].LessThan(b.Value[j])
if err != nil {
return false
}
return less
})

var res ListType
p1, p2 := 0, 0

for p1 < len(a.Value) && p2 < len(b.Value) {
lt, err := a.Value[p1].LessThan(b.Value[p2])
if err != nil {
return nil, err
}
eq, err := a.Value[p1].Equal(b.Value[p2])
if err != nil {
return nil, err
}

if lt {
p1++
} else if eq {
res.Value = append(res.Value, a.Value[p1])
p1++
p2++
} else {
p2++
}
}

return &res, nil
}

func unite(a *ListType, b *ListType) (*ListType, error) {
// intersect the two lists
sort.Slice(a.Value, func(i, j int) bool {
less, err := a.Value[i].LessThan(a.Value[j])
if err != nil {
return false
}
return less
})

sort.Slice(b.Value, func(i, j int) bool {
less, err := b.Value[i].LessThan(b.Value[j])
if err != nil {
return false
}
return less
})

var res ListType
p1, p2 := 0, 0

for p1 < len(a.Value) && p2 < len(b.Value) {
lt, err := a.Value[p1].LessThan(b.Value[p2])
if err != nil {
return nil, err
}
eq, err := a.Value[p1].Equal(b.Value[p2])
if err != nil {
return nil, err
}

if lt {
res.Value = append(res.Value, a.Value[p1])
p1++
} else if eq {
res.Value = append(res.Value, a.Value[p1])
p1++
p2++
} else {
res.Value = append(res.Value, b.Value[p2])
p2++
}
}

for p1 < len(a.Value) {
res.Value = append(res.Value, a.Value[p1])
p1++
}
for p2 < len(b.Value) {
res.Value = append(res.Value, b.Value[p2])
p2++
}

return res, nil
return &res, nil
}
72 changes: 72 additions & 0 deletions types/list_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package types

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestAndBetweenTwoDifferentLists(t *testing.T) {
l1, _ := NewList([]BaseType{NewInt(1), NewInt(2)})
l2, _ := NewList([]BaseType{NewBool(true), NewBool(false)})

r, err := l1.And(l2)
assert.Error(t, err)
assert.Nil(t, r)
}

func TestAndBetweenTwoIntLists(t *testing.T) {
l1, _ := NewList([]BaseType{NewInt(1), NewInt(2)})
l2, _ := NewList([]BaseType{NewInt(2), NewInt(3)})

r, err := l1.And(l2)
assert.NoError(t, err)

res := r.(*ListType)
assert.Len(t, res.Value, 1)

assert.Equal(t, 2, res.Value[0].(*IntType).Value)
}

func TestOrBetweenTwoIntLists(t *testing.T) {
l1, _ := NewList([]BaseType{NewInt(1), NewInt(2)})
l2, _ := NewList([]BaseType{NewInt(2), NewInt(3)})

r, err := l1.Or(l2)
assert.NoError(t, err)

res := r.(*ListType)
assert.Len(t, res.Value, 3)

assert.Equal(t, 1, res.Value[0].(*IntType).Value)
assert.Equal(t, 2, res.Value[1].(*IntType).Value)
assert.Equal(t, 3, res.Value[2].(*IntType).Value)
}

func TestAndBetweenTwoUserDefinedLists(t *testing.T) {
l1, _ := NewList([]BaseType{NewUserDefined("Person", map[string]BaseType{"age": NewInt(10), "isEmployeed": NewBool(true)})})
l2, _ := NewList([]BaseType{NewUserDefined("Person", map[string]BaseType{"age": NewInt(12), "isEmployeed": NewBool(true)})})

r, err := l1.And(l2)
assert.NoError(t, err)

res := r.(*ListType)
assert.Len(t, res.Value, 0)
}

func TestOrBetweenTwoUserDefinedLists(t *testing.T) {
l1, _ := NewList([]BaseType{NewUserDefined("Person", map[string]BaseType{"age": NewInt(10), "name": NewString("a")})})
l2, _ := NewList([]BaseType{NewUserDefined("Person", map[string]BaseType{"age": NewInt(12), "name": NewString("b")})})

r, err := l1.Or(l2)
assert.NoError(t, err)

res := r.(*ListType)
assert.Len(t, res.Value, 2)

assert.Equal(t, 10, res.Value[0].(*UserDefinedType).Properties["age"].(*IntType).Value)
assert.Equal(t, "a", res.Value[0].(*UserDefinedType).Properties["name"].(*StringType).Value)

assert.Equal(t, 12, res.Value[1].(*UserDefinedType).Properties["age"].(*IntType).Value)
assert.Equal(t, "b", res.Value[1].(*UserDefinedType).Properties["name"].(*StringType).Value)
}

0 comments on commit 5317e1f

Please sign in to comment.