From 5b1265e99439f408a0a7595c67b70ded61713439 Mon Sep 17 00:00:00 2001 From: halfrost Date: Tue, 14 Jun 2022 16:46:18 -0700 Subject: [PATCH] Add modeling implement Signed-off-by: halfrost --- go.mod | 1 + go.sum | 2 + pkg/modeling/modeling.go | 327 +++++++ pkg/modeling/modeling_test.go | 893 ++++++++++++++++++ vendor/github.com/emirpasic/gods/LICENSE | 41 + .../emirpasic/gods/containers/containers.go | 36 + .../emirpasic/gods/containers/enumerable.go | 57 ++ .../emirpasic/gods/containers/iterator.go | 133 +++ .../gods/containers/serialization.go | 21 + .../gods/trees/redblacktree/iterator.go | 190 ++++ .../gods/trees/redblacktree/redblacktree.go | 548 +++++++++++ .../gods/trees/redblacktree/serialization.go | 48 + .../github.com/emirpasic/gods/trees/trees.go | 22 + .../emirpasic/gods/utils/comparator.go | 251 +++++ .../github.com/emirpasic/gods/utils/sort.go | 29 + .../github.com/emirpasic/gods/utils/utils.go | 47 + vendor/modules.txt | 6 + 17 files changed, 2652 insertions(+) create mode 100644 pkg/modeling/modeling.go create mode 100644 pkg/modeling/modeling_test.go create mode 100644 vendor/github.com/emirpasic/gods/LICENSE create mode 100644 vendor/github.com/emirpasic/gods/containers/containers.go create mode 100644 vendor/github.com/emirpasic/gods/containers/enumerable.go create mode 100644 vendor/github.com/emirpasic/gods/containers/iterator.go create mode 100644 vendor/github.com/emirpasic/gods/containers/serialization.go create mode 100644 vendor/github.com/emirpasic/gods/trees/redblacktree/iterator.go create mode 100644 vendor/github.com/emirpasic/gods/trees/redblacktree/redblacktree.go create mode 100644 vendor/github.com/emirpasic/gods/trees/redblacktree/serialization.go create mode 100644 vendor/github.com/emirpasic/gods/trees/trees.go create mode 100644 vendor/github.com/emirpasic/gods/utils/comparator.go create mode 100644 vendor/github.com/emirpasic/gods/utils/sort.go create mode 100644 vendor/github.com/emirpasic/gods/utils/utils.go diff --git a/go.mod b/go.mod index e3868f6ea..01657aec9 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.18 require ( github.com/distribution/distribution/v3 v3.0.0-20210507173845-9329f6a62b67 + github.com/emirpasic/gods v1.18.1 github.com/evanphx/json-patch/v5 v5.6.0 github.com/gogo/protobuf v1.3.2 github.com/google/go-cmp v0.5.6 diff --git a/go.sum b/go.sum index 62b0b4908..7d36aaabd 100644 --- a/go.sum +++ b/go.sum @@ -221,6 +221,8 @@ github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkg github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= diff --git a/pkg/modeling/modeling.go b/pkg/modeling/modeling.go new file mode 100644 index 000000000..5df9b56bb --- /dev/null +++ b/pkg/modeling/modeling.go @@ -0,0 +1,327 @@ +package modeling + +import ( + "container/list" + "errors" + "math" + "sync" + + rbt "github.com/emirpasic/gods/trees/redblacktree" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/klog/v2" + + clusterapis "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1" +) + +var ( + mu sync.Mutex + modelSortings [][]resource.Quantity + defaultModelSorting []clusterapis.ResourceName +) + +// ResourceSummary records the list of resourceModels +type ResourceSummary []resourceModels + +// resourceModels records the number of each allocatable resource models. +type resourceModels struct { + // quantity is the total number of each allocatable resource models + // +required + Quantity int + + // when the number of node is less than or equal to six, it will be sorted by linkedlist, + // when the number of node is more than six, it will be sorted by red-black tree. + + // when the data structure is linkedlist, + // each item will store clusterResourceNode. + // +required + linkedlist *list.List + + // when the data structure is redblack tree, + // each item will store a key-value pair, + // key is ResourceList, the value is quantity of this ResourceList + // +optional + redblackTree *rbt.Tree +} + +// ClusterResourceNode represents the each raw resource entity without modeling. +type ClusterResourceNode struct { + // quantity is the the number of this node + // Only when the resourceLists are exactly the same can they be counted as the same node. + // +required + quantity int + + // resourceList records the resource list of this node. + // It maybe contain cpu, mrmory, gpu... + // User can specify which parameters need to be included before the cluster starts + // +required + resourceList ResourceList +} + +// ResourceList is a set of (resource name, quantity) pairs. +type ResourceList map[clusterapis.ResourceName]resource.Quantity + +// InitSummary is the init function of modeling data structure +func InitSummary(resourceModels []clusterapis.ResourceModel) (ResourceSummary, error) { + rsName, rsList := []clusterapis.ResourceName{}, []ResourceList{} + for _, rm := range resourceModels { + tmp := map[clusterapis.ResourceName]resource.Quantity{} + for _, rmItem := range rm.Ranges { + if len(rsName) != len(rm.Ranges) { + rsName = append(rsName, rmItem.Name) + } + tmp[rmItem.Name] = rmItem.Min + } + rsList = append(rsList, tmp) + } + + if len(rsName) != 0 && len(rsList) != 0 && (len(rsName) != len(rsList[0])) { + return nil, errors.New("the number of resourceName is not equal the number of resourceList") + } + var rs ResourceSummary + if len(rsName) != 0 { + defaultModelSorting = rsName + } + rs = make(ResourceSummary, len(rsList)) + // generate a sorted array by first priority of ResourceName + modelSortings = make([][]resource.Quantity, len(rsList)) + for index := 0; index < len(rsList); index++ { + for i, name := range rsName { + modelSortings[i] = append(modelSortings[i], rsList[index][name]) + } + } + return rs, nil +} + +// NewClusterResourceNode create new cluster resource node +func NewClusterResourceNode(resourceList corev1.ResourceList) ClusterResourceNode { + return ClusterResourceNode{ + quantity: 1, + resourceList: ConvertToResourceList(resourceList), + } +} + +func (rs *ResourceSummary) getIndex(crn ClusterResourceNode) int { + index := math.MaxInt + for i, m := range defaultModelSorting { + tmpIndex := searchLastLessElement(modelSortings[i], crn.resourceList[m]) + if tmpIndex < index { + index = tmpIndex + } + } + return index +} + +func searchLastLessElement(nums []resource.Quantity, target resource.Quantity) int { + low, high := 0, len(nums)-1 + for low <= high { + mid := low + ((high - low) >> 1) + diff1 := nums[mid].Cmp(target) + var diff2 int + if mid != len(nums)-1 { + diff2 = nums[mid+1].Cmp(target) + } + // diff < 1 means nums[mid] <= target + // diff == 1 means nums[mid+1] > target + if diff1 < 1 { + if (mid == len(nums)-1) || (diff2 == 1) { + // find the last less element that equal to target element + return mid + } + low = mid + 1 + } else { + high = mid - 1 + } + } + return -1 +} + +// clusterResourceNodeComparator provides a fast comparison on clusterResourceNodes +func clusterResourceNodeComparator(a, b interface{}) int { + s1 := a.(ClusterResourceNode) + s2 := b.(ClusterResourceNode) + for index := 0; index < len(defaultModelSorting); index++ { + tmp1, tmp2 := s1.resourceList[defaultModelSorting[index]], s2.resourceList[defaultModelSorting[index]] + + diff := tmp1.Cmp(tmp2) + if diff != 0 { + return diff + } + } + return 0 +} + +func safeChangeNum(num *int, change int) { + mu.Lock() + defer mu.Unlock() + + (*num) += change +} + +// AddToResourceSummary add resource node into modeling summary +func (rs *ResourceSummary) AddToResourceSummary(crn ClusterResourceNode) { + index := rs.getIndex(crn) + modeling := &(*rs)[index] + if rs.GetNodeNumFromModel(modeling) <= 5 { + root := modeling.linkedlist + if root == nil { + root = list.New() + } + found := false + // traverse linkedlist to add quantity of recourse modeling + for element := root.Front(); element != nil; element = element.Next() { + switch clusterResourceNodeComparator(element.Value, crn) { + case 0: + { + tmpCrn := element.Value.(ClusterResourceNode) + safeChangeNum(&tmpCrn.quantity, crn.quantity) + element.Value = tmpCrn + found = true + break + } + case 1: + { + root.InsertBefore(crn, element) + found = true + break + } + case -1: + { + continue + } + } + if found { + break + } + } + if !found { + root.PushBack(crn) + } + modeling.linkedlist = root + } else { + root := modeling.redblackTree + if root == nil { + root = llConvertToRbt(modeling.linkedlist) + modeling.linkedlist = nil + } + tmpNode := root.GetNode(crn) + if tmpNode != nil { + node := tmpNode.Key.(ClusterResourceNode) + safeChangeNum(&node.quantity, crn.quantity) + tmpNode.Key = node + } else { + root.Put(crn, crn.quantity) + } + modeling.redblackTree = root + } + safeChangeNum(&modeling.Quantity, crn.quantity) +} + +func llConvertToRbt(list *list.List) *rbt.Tree { + root := rbt.NewWith(clusterResourceNodeComparator) + for element := list.Front(); element != nil; element = element.Next() { + tmpCrn := element.Value.(ClusterResourceNode) + root.Put(tmpCrn, tmpCrn.quantity) + } + return root +} + +func rbtConvertToLl(rbt *rbt.Tree) *list.List { + root := list.New() + for _, v := range rbt.Keys() { + root.PushBack(v) + } + return root +} + +// ConvertToResourceList is convert from corev1.ResourceList to ResourceList +func ConvertToResourceList(rslist corev1.ResourceList) ResourceList { + resourceList := ResourceList{} + for name, quantity := range rslist { + if name == corev1.ResourceCPU { + resourceList[clusterapis.ResourceCPU] = quantity + } else if name == corev1.ResourceMemory { + resourceList[clusterapis.ResourceMemory] = quantity + } else if name == corev1.ResourceStorage { + resourceList[clusterapis.ResourceStorage] = quantity + } else if name == corev1.ResourceEphemeralStorage { + resourceList[clusterapis.ResourceEphemeralStorage] = quantity + } + } + return resourceList +} + +// GetNodeNumFromModel is for getting node number from the modeling +func (rs *ResourceSummary) GetNodeNumFromModel(model *resourceModels) int { + if model.linkedlist != nil && model.redblackTree == nil { + return model.linkedlist.Len() + } else if model.linkedlist == nil && model.redblackTree != nil { + return model.redblackTree.Size() + } else if model.linkedlist == nil && model.redblackTree == nil { + return 0 + } else if model.linkedlist != nil && model.redblackTree != nil { + klog.Info("getNodeNum: unknow error") + } + return 0 +} + +// DeleteFromResourceSummary dalete resource node into modeling summary +func (rs *ResourceSummary) DeleteFromResourceSummary(crn ClusterResourceNode) error { + index := rs.getIndex(crn) + modeling := &(*rs)[index] + if rs.GetNodeNumFromModel(modeling) >= 6 { + root := modeling.redblackTree + tmpNode := root.GetNode(crn) + if tmpNode != nil { + node := tmpNode.Key.(ClusterResourceNode) + safeChangeNum(&node.quantity, -crn.quantity) + tmpNode.Key = node + if node.quantity == 0 { + root.Remove(tmpNode) + } + } else { + return errors.New("delete fail: node no found in redblack tree") + } + modeling.redblackTree = root + } else { + root, tree := modeling.linkedlist, modeling.redblackTree + if root == nil && tree != nil { + root = rbtConvertToLl(tree) + } + if root == nil && tree == nil { + return errors.New("delete fail: node no found in linked list") + } + found := false + // traverse linkedlist to remove quantity of recourse modeling + for element := root.Front(); element != nil; element = element.Next() { + if clusterResourceNodeComparator(element.Value, crn) == 0 { + tmpCrn := element.Value.(ClusterResourceNode) + safeChangeNum(&tmpCrn.quantity, -crn.quantity) + element.Value = tmpCrn + if tmpCrn.quantity == 0 { + root.Remove(element) + } + found = true + } + if found { + break + } + } + if !found { + return errors.New("delete fail: node no found in linkedlist") + } + modeling.linkedlist = root + } + safeChangeNum(&modeling.Quantity, -crn.quantity) + return nil +} + +// UpdateInResourceSummary update resource node into modeling summary +func (rs *ResourceSummary) UpdateInResourceSummary(oldNode, newNode ClusterResourceNode) error { + rs.AddToResourceSummary(newNode) + err := rs.DeleteFromResourceSummary(oldNode) + if err != nil { + return errors.New("delete fail: node no found in linked list") + } + return nil +} diff --git a/pkg/modeling/modeling_test.go b/pkg/modeling/modeling_test.go new file mode 100644 index 000000000..f13f56deb --- /dev/null +++ b/pkg/modeling/modeling_test.go @@ -0,0 +1,893 @@ +package modeling + +import ( + "container/list" + "fmt" + "reflect" + "testing" + + rbt "github.com/emirpasic/gods/trees/redblacktree" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + + clusterapis "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1" +) + +func TestInitSummary(t *testing.T) { + rms := []clusterapis.ResourceModel{ + { + Grade: 0, + Ranges: []clusterapis.ResourceModelRange{ + { + Name: clusterapis.ResourceCPU, + Min: *resource.NewMilliQuantity(0, resource.DecimalSI), + Max: *resource.NewQuantity(1, resource.DecimalSI), + }, + { + Name: clusterapis.ResourceMemory, + Min: *resource.NewMilliQuantity(0, resource.DecimalSI), + Max: *resource.NewQuantity(1024, resource.DecimalSI), + }, + }, + }, + { + Grade: 1, + Ranges: []clusterapis.ResourceModelRange{ + { + Name: clusterapis.ResourceCPU, + Min: *resource.NewMilliQuantity(1, resource.DecimalSI), + Max: *resource.NewQuantity(2, resource.DecimalSI), + }, + { + Name: clusterapis.ResourceMemory, + Min: *resource.NewMilliQuantity(1024, resource.DecimalSI), + Max: *resource.NewQuantity(1024*2, resource.DecimalSI), + }, + }, + }, + } + + rs, err := InitSummary(rms) + if actualValue := len(rs); actualValue != 2 { + t.Errorf("Got %v expected %v", actualValue, 2) + } + + if err != nil { + t.Errorf("Got %v expected %v", err, nil) + } +} + +func TestInitSummaryError(t *testing.T) { + rms := []clusterapis.ResourceModel{ + { + Grade: 0, + Ranges: []clusterapis.ResourceModelRange{ + { + Name: clusterapis.ResourceCPU, + Min: *resource.NewMilliQuantity(0, resource.DecimalSI), + Max: *resource.NewQuantity(1, resource.DecimalSI), + }, + { + Name: clusterapis.ResourceMemory, + Min: *resource.NewMilliQuantity(0, resource.DecimalSI), + Max: *resource.NewQuantity(1024, resource.DecimalSI), + }, + }, + }, + { + Grade: 1, + Ranges: []clusterapis.ResourceModelRange{ + { + Name: clusterapis.ResourceCPU, + Min: *resource.NewMilliQuantity(1, resource.DecimalSI), + Max: *resource.NewQuantity(2, resource.DecimalSI), + }, + }, + }, + } + + rs, err := InitSummary(rms) + if actualValue := len(rs); actualValue != 0 { + t.Errorf("Got %v expected %v", actualValue, 0) + } + + if err == nil { + t.Errorf("Got %v expected %v", err, nil) + } +} + +func TestSearchLastLessElement(t *testing.T) { + nums, target := []resource.Quantity{ + *resource.NewMilliQuantity(1999, resource.DecimalSI), + *resource.NewMilliQuantity(4327, resource.DecimalSI), + *resource.NewMilliQuantity(9783, resource.DecimalSI), + *resource.NewMilliQuantity(12378, resource.DecimalSI), + *resource.NewMilliQuantity(23480, resource.DecimalSI), + *resource.NewMilliQuantity(74217, resource.DecimalSI), + }, *resource.NewMilliQuantity(10000, resource.DecimalSI) + index := searchLastLessElement(nums, target) + if index != 2 { + t.Errorf("Got %v expected %v", index, 2) + } + nums, target = []resource.Quantity{ + *resource.NewMilliQuantity(1000, resource.DecimalSI), + *resource.NewMilliQuantity(3400, resource.DecimalSI), + *resource.NewMilliQuantity(4500, resource.DecimalSI), + *resource.NewMilliQuantity(8700, resource.DecimalSI), + *resource.NewMilliQuantity(11599, resource.DecimalSI), + *resource.NewMilliQuantity(62300, resource.DecimalSI), + *resource.NewMilliQuantity(95600, resource.DecimalSI), + *resource.NewMilliQuantity(134600, resource.DecimalSI), + }, *resource.NewMilliQuantity(9700, resource.DecimalSI) + index = searchLastLessElement(nums, target) + if index != 3 { + t.Errorf("Got %v expected %v", index, 3) + } + nums, target = []resource.Quantity{ + *resource.NewMilliQuantity(1000, resource.DecimalSI), + *resource.NewMilliQuantity(2000, resource.DecimalSI), + }, *resource.NewMilliQuantity(0, resource.DecimalSI) + index = searchLastLessElement(nums, target) + if index != -1 { + t.Errorf("Got %v expected %v", index, -1) + } +} + +func TestGetIndex(t *testing.T) { + rms := []clusterapis.ResourceModel{ + { + Grade: 0, + Ranges: []clusterapis.ResourceModelRange{ + { + Name: clusterapis.ResourceCPU, + Min: *resource.NewMilliQuantity(0, resource.DecimalSI), + Max: *resource.NewQuantity(1, resource.DecimalSI), + }, + { + Name: clusterapis.ResourceMemory, + Min: *resource.NewMilliQuantity(0, resource.DecimalSI), + Max: *resource.NewQuantity(1024, resource.DecimalSI), + }, + }, + }, + { + Grade: 1, + Ranges: []clusterapis.ResourceModelRange{ + { + Name: clusterapis.ResourceCPU, + Min: *resource.NewMilliQuantity(1, resource.DecimalSI), + Max: *resource.NewQuantity(2, resource.DecimalSI), + }, + { + Name: clusterapis.ResourceMemory, + Min: *resource.NewMilliQuantity(1024, resource.DecimalSI), + Max: *resource.NewQuantity(1024*2, resource.DecimalSI), + }, + }, + }, + } + + rs, err := InitSummary(rms) + + if err != nil { + t.Errorf("Got %v expected %v", err, nil) + } + + crn := ClusterResourceNode{ + quantity: 1, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(1, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(1024, resource.DecimalSI), + }, + } + index := rs.getIndex(crn) + + if index != 1 { + t.Errorf("Got %v expected %v", index, 1) + } + + crn = ClusterResourceNode{ + quantity: 1, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(20, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(1024*100, resource.DecimalSI), + }, + } + index = rs.getIndex(crn) + + if index != 1 { + t.Errorf("Got %v expected %v", index, 1) + } +} + +func TestClusterResourceNodeComparator(t *testing.T) { + crn1 := ClusterResourceNode{ + quantity: 10, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(10, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(1024, resource.DecimalSI), + }, + } + + crn2 := ClusterResourceNode{ + quantity: 789, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(2, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(1024, resource.DecimalSI), + }, + } + if res := clusterResourceNodeComparator(crn1, crn2); res != 1 { + t.Errorf("Got %v expected %v", res, 1) + } + + crn1 = ClusterResourceNode{ + quantity: 10, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(6, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(1024, resource.DecimalSI), + }, + } + + crn2 = ClusterResourceNode{ + quantity: 789, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(6, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(1024, resource.DecimalSI), + }, + } + if res := clusterResourceNodeComparator(crn1, crn2); res != 0 { + t.Errorf("Got %v expected %v", res, 0) + } + + crn1 = ClusterResourceNode{ + quantity: 10, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(6, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(1024, resource.DecimalSI), + }, + } + + crn2 = ClusterResourceNode{ + quantity: 789, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(6, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(1024*10, resource.DecimalSI), + }, + } + if res := clusterResourceNodeComparator(crn1, crn2); res != -1 { + t.Errorf("Got %v expected %v", res, -1) + } +} + +func TestGetNodeNumFromModel(t *testing.T) { + rms := []clusterapis.ResourceModel{ + { + Grade: 0, + Ranges: []clusterapis.ResourceModelRange{ + { + Name: clusterapis.ResourceCPU, + Min: *resource.NewMilliQuantity(0, resource.DecimalSI), + Max: *resource.NewQuantity(1, resource.DecimalSI), + }, + { + Name: clusterapis.ResourceMemory, + Min: *resource.NewMilliQuantity(0, resource.DecimalSI), + Max: *resource.NewQuantity(1024, resource.DecimalSI), + }, + }, + }, + { + Grade: 1, + Ranges: []clusterapis.ResourceModelRange{ + { + Name: clusterapis.ResourceCPU, + Min: *resource.NewMilliQuantity(1, resource.DecimalSI), + Max: *resource.NewQuantity(2, resource.DecimalSI), + }, + { + Name: clusterapis.ResourceMemory, + Min: *resource.NewMilliQuantity(1024, resource.DecimalSI), + Max: *resource.NewQuantity(1024*2, resource.DecimalSI), + }, + }, + }, + } + + rs, err := InitSummary(rms) + if actualValue := len(rs); actualValue != 2 { + t.Errorf("Got %v expected %v", actualValue, 2) + } + + if err != nil { + t.Errorf("Got %v expected %v", err, nil) + } + + crn1 := ClusterResourceNode{ + quantity: 3, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(8, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(1024*3, resource.DecimalSI), + }, + } + + crn2 := ClusterResourceNode{ + quantity: 1, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(1, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(1024*6, resource.DecimalSI), + }, + } + + crn3 := ClusterResourceNode{ + quantity: 2, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(1, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(1024*6, resource.DecimalSI), + }, + } + + rs.AddToResourceSummary(crn1) + rs.AddToResourceSummary(crn2) + rs.AddToResourceSummary(crn3) + + for index := range rs { + num := rs.GetNodeNumFromModel(&rs[index]) + if index == 0 { + if num != 0 { + t.Errorf("Got %v expected %v", num, 0) + } + } + if index == 1 { + if num != 2 { + t.Errorf("Got %v expected %v", num, 2) + } + } + } +} + +func TestConvertToResourceList(t *testing.T) { + rslist := corev1.ResourceList{ + corev1.ResourceCPU: *resource.NewMilliQuantity(1, resource.DecimalSI), + corev1.ResourceMemory: *resource.NewQuantity(1024*6, resource.DecimalSI), + } + rsl := ConvertToResourceList(rslist) + for name := range rsl { + if reflect.TypeOf(name).String() != "v1alpha1.ResourceName" { + t.Errorf("Got %v expected %v", reflect.TypeOf(name), "v1alpha1.ResourceName") + } + } +} + +func TestLlConvertToRbt(t *testing.T) { + crn1 := ClusterResourceNode{ + quantity: 6, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(2, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(1024*7, resource.DecimalSI), + }, + } + + crn2 := ClusterResourceNode{ + quantity: 5, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(6, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(1024*3, resource.DecimalSI), + }, + } + + crn3 := ClusterResourceNode{ + quantity: 4, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(5, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(1024*8, resource.DecimalSI), + }, + } + + crn4 := ClusterResourceNode{ + quantity: 3, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(8, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(1024*3, resource.DecimalSI), + }, + } + + crn5 := ClusterResourceNode{ + quantity: 2, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(1, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(1024*6, resource.DecimalSI), + }, + } + + crn6 := ClusterResourceNode{ + quantity: 1, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(2, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(1024*12, resource.DecimalSI), + }, + } + mylist := list.New() + mylist.PushBack(crn5) + mylist.PushBack(crn1) + mylist.PushBack(crn6) + mylist.PushBack(crn3) + mylist.PushBack(crn2) + mylist.PushBack(crn4) + + rbt := llConvertToRbt(mylist) + fmt.Println(rbt) + if actualValue := rbt.Size(); actualValue != 6 { + t.Errorf("Got %v expected %v", actualValue, 6) + } + + actualValue := rbt.GetNode(crn5) + node := actualValue.Key.(ClusterResourceNode) + if quantity := node.quantity; quantity != 2 { + t.Errorf("Got %v expected %v", actualValue, 2) + } + + actualValue = rbt.GetNode(crn6) + node = actualValue.Key.(ClusterResourceNode) + if quantity := node.quantity; quantity != 1 { + t.Errorf("Got %v expected %v", actualValue, 1) + } + + actualValue = rbt.GetNode(crn1) + node = actualValue.Key.(ClusterResourceNode) + if quantity := node.quantity; quantity != 6 { + t.Errorf("Got %v expected %v", actualValue, 6) + } +} + +func TestRbtConvertToLl(t *testing.T) { + crn1 := ClusterResourceNode{ + quantity: 6, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(2, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(1024*7, resource.DecimalSI), + }, + } + + crn2 := ClusterResourceNode{ + quantity: 5, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(6, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(1024*3, resource.DecimalSI), + }, + } + + crn3 := ClusterResourceNode{ + quantity: 4, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(5, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(1024*8, resource.DecimalSI), + }, + } + + crn4 := ClusterResourceNode{ + quantity: 3, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(8, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(1024*3, resource.DecimalSI), + }, + } + + crn5 := ClusterResourceNode{ + quantity: 2, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(1, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(1024*6, resource.DecimalSI), + }, + } + + crn6 := ClusterResourceNode{ + quantity: 1, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(2, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(1024*12, resource.DecimalSI), + }, + } + tree := rbt.NewWith(clusterResourceNodeComparator) + + if actualValue := tree.Size(); actualValue != 0 { + t.Errorf("Got %v expected %v", actualValue, 0) + } + + if actualValue := tree.GetNode(2).Size(); actualValue != 0 { + t.Errorf("Got %v expected %v", actualValue, 0) + } + + tree.Put(crn2, crn2.quantity) + tree.Put(crn1, crn1.quantity) + tree.Put(crn6, crn6.quantity) + tree.Put(crn3, crn3.quantity) + tree.Put(crn5, crn5.quantity) + tree.Put(crn4, crn4.quantity) + + ll := rbtConvertToLl(tree) + fmt.Println(ll) + + for element := ll.Front(); element != nil; element = element.Next() { + fmt.Println(element.Value) + } + + actualValue := ll.Front() + node := actualValue.Value.(ClusterResourceNode) + if quantity := node.quantity; quantity != 2 { + t.Errorf("Got %v expected %v", actualValue, 2) + } + ll.Remove(actualValue) + + actualValue = ll.Front() + node = actualValue.Value.(ClusterResourceNode) + if quantity := node.quantity; quantity != 6 { + t.Errorf("Got %v expected %v", actualValue, 6) + } + ll.Remove(actualValue) + + actualValue = ll.Front() + node = actualValue.Value.(ClusterResourceNode) + if quantity := node.quantity; quantity != 1 { + t.Errorf("Got %v expected %v", actualValue, 1) + } + ll.Remove(actualValue) + + actualValue = ll.Front() + node = actualValue.Value.(ClusterResourceNode) + if quantity := node.quantity; quantity != 4 { + t.Errorf("Got %v expected %v", actualValue, 4) + } + ll.Remove(actualValue) + + actualValue = ll.Front() + node = actualValue.Value.(ClusterResourceNode) + if quantity := node.quantity; quantity != 5 { + t.Errorf("Got %v expected %v", actualValue, 5) + } + ll.Remove(actualValue) + + actualValue = ll.Front() + node = actualValue.Value.(ClusterResourceNode) + if quantity := node.quantity; quantity != 3 { + t.Errorf("Got %v expected %v", actualValue, 3) + } + ll.Remove(actualValue) + + if actualValue := ll.Len(); actualValue != 0 { + t.Errorf("Got %v expected %v", actualValue, 0) + } +} + +func TestAddToResourceSummary(t *testing.T) { + rms := []clusterapis.ResourceModel{ + { + Grade: 0, + Ranges: []clusterapis.ResourceModelRange{ + { + Name: clusterapis.ResourceCPU, + Min: *resource.NewMilliQuantity(0, resource.DecimalSI), + Max: *resource.NewQuantity(1, resource.DecimalSI), + }, + { + Name: clusterapis.ResourceMemory, + Min: *resource.NewMilliQuantity(0, resource.DecimalSI), + Max: *resource.NewQuantity(1024, resource.DecimalSI), + }, + }, + }, + { + Grade: 1, + Ranges: []clusterapis.ResourceModelRange{ + { + Name: clusterapis.ResourceCPU, + Min: *resource.NewMilliQuantity(1, resource.DecimalSI), + Max: *resource.NewQuantity(2, resource.DecimalSI), + }, + { + Name: clusterapis.ResourceMemory, + Min: *resource.NewMilliQuantity(1024, resource.DecimalSI), + Max: *resource.NewQuantity(1024*2, resource.DecimalSI), + }, + }, + }, + } + + rs, err := InitSummary(rms) + if actualValue := len(rs); actualValue != 2 { + t.Errorf("Got %v expected %v", actualValue, 2) + } + + if err != nil { + t.Errorf("Got %v expected %v", err, nil) + } + + crn1 := ClusterResourceNode{ + quantity: 3, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(8, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(1024*3, resource.DecimalSI), + }, + } + + crn2 := ClusterResourceNode{ + quantity: 1, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(1, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(1024*6, resource.DecimalSI), + }, + } + + crn3 := ClusterResourceNode{ + quantity: 2, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(1, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(1024*6, resource.DecimalSI), + }, + } + + rs.AddToResourceSummary(crn1) + rs.AddToResourceSummary(crn2) + rs.AddToResourceSummary(crn3) + + for index, v := range rs { + num := rs.GetNodeNumFromModel(&rs[index]) + if index == 0 && num != 0 { + t.Errorf("Got %v expected %v", num, 0) + } + if index == 1 && num != 2 { + t.Errorf("Got %v expected %v", num, 2) + } + if index == 0 && v.Quantity != 0 { + t.Errorf("Got %v expected %v", v.Quantity, 0) + } + if index == 1 && v.Quantity != 6 { + t.Errorf("Got %v expected %v", v.Quantity, 6) + } + } + + crn4 := ClusterResourceNode{ + quantity: 2, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(0, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(19, resource.DecimalSI), + }, + } + + rs.AddToResourceSummary(crn4) + + if actualValue := rs[0]; actualValue.Quantity != 2 { + t.Errorf("Got %v expected %v", actualValue, 2) + } + + if actualValue := rs[0]; rs.GetNodeNumFromModel(&actualValue) != 1 { + t.Errorf("Got %v expected %v", actualValue, 1) + } +} + +func TestDeleteFromResourceSummary(t *testing.T) { + rms := []clusterapis.ResourceModel{ + { + Grade: 0, + Ranges: []clusterapis.ResourceModelRange{ + { + Name: clusterapis.ResourceCPU, + Min: *resource.NewMilliQuantity(0, resource.DecimalSI), + Max: *resource.NewQuantity(1, resource.DecimalSI), + }, + { + Name: clusterapis.ResourceMemory, + Min: *resource.NewMilliQuantity(0, resource.DecimalSI), + Max: *resource.NewQuantity(1024, resource.DecimalSI), + }, + }, + }, + { + Grade: 1, + Ranges: []clusterapis.ResourceModelRange{ + { + Name: clusterapis.ResourceCPU, + Min: *resource.NewMilliQuantity(1, resource.DecimalSI), + Max: *resource.NewQuantity(2, resource.DecimalSI), + }, + { + Name: clusterapis.ResourceMemory, + Min: *resource.NewMilliQuantity(1024, resource.DecimalSI), + Max: *resource.NewQuantity(1024*2, resource.DecimalSI), + }, + }, + }, + } + + rs, err := InitSummary(rms) + if actualValue := len(rs); actualValue != 2 { + t.Errorf("Got %v expected %v", actualValue, 2) + } + + if err != nil { + t.Errorf("Got %v expected %v", err, nil) + } + + crn1 := ClusterResourceNode{ + quantity: 3, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(8, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(1024*3, resource.DecimalSI), + }, + } + + crn2 := ClusterResourceNode{ + quantity: 1, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(1, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(1024*6, resource.DecimalSI), + }, + } + + crn3 := ClusterResourceNode{ + quantity: 2, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(1, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(1024*6, resource.DecimalSI), + }, + } + + rs.AddToResourceSummary(crn1) + rs.AddToResourceSummary(crn2) + rs.AddToResourceSummary(crn3) + + for index := range rs { + num := rs.GetNodeNumFromModel(&rs[index]) + if index == 0 && num != 0 { + t.Errorf("Got %v expected %v", num, 0) + } + if index == 1 && num != 2 { + t.Errorf("Got %v expected %v", num, 2) + } + if index == 0 && rs[index].Quantity != 0 { + t.Errorf("Got %v expected %v", rs[index].Quantity, 0) + } + if index == 1 && rs[index].Quantity != 6 { + t.Errorf("Got %v expected %v", rs[index].Quantity, 6) + } + } + + crn4 := ClusterResourceNode{ + quantity: 2, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(1, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(19, resource.DecimalSI), + }, + } + + err = rs.DeleteFromResourceSummary(crn4) + + if err == nil { + t.Errorf("Got %v expected %v", err, nil) + } + + if actualValue := rs[1]; actualValue.Quantity != 6 { + t.Errorf("Got %v expected %v", actualValue, 6) + } + + if actualValue := rs[0]; rs.GetNodeNumFromModel(&actualValue) != 0 { + t.Errorf("Got %v expected %v", actualValue, 0) + } +} + +func TestUpdateSummary(t *testing.T) { + rms := []clusterapis.ResourceModel{ + { + Grade: 0, + Ranges: []clusterapis.ResourceModelRange{ + { + Name: clusterapis.ResourceCPU, + Min: *resource.NewMilliQuantity(0, resource.DecimalSI), + Max: *resource.NewQuantity(1, resource.DecimalSI), + }, + { + Name: clusterapis.ResourceMemory, + Min: *resource.NewMilliQuantity(0, resource.DecimalSI), + Max: *resource.NewQuantity(1024, resource.DecimalSI), + }, + }, + }, + { + Grade: 1, + Ranges: []clusterapis.ResourceModelRange{ + { + Name: clusterapis.ResourceCPU, + Min: *resource.NewMilliQuantity(1, resource.DecimalSI), + Max: *resource.NewQuantity(2, resource.DecimalSI), + }, + { + Name: clusterapis.ResourceMemory, + Min: *resource.NewMilliQuantity(1024, resource.DecimalSI), + Max: *resource.NewQuantity(1024*2, resource.DecimalSI), + }, + }, + }, + } + + rs, err := InitSummary(rms) + if actualValue := len(rs); actualValue != 2 { + t.Errorf("Got %v expected %v", actualValue, 2) + } + + if err != nil { + t.Errorf("Got %v expected %v", err, nil) + } + + crn1 := ClusterResourceNode{ + quantity: 3, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(8, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(1024*3, resource.DecimalSI), + }, + } + + crn2 := ClusterResourceNode{ + quantity: 1, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(1, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(1024*6, resource.DecimalSI), + }, + } + + crn3 := ClusterResourceNode{ + quantity: 2, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(1, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(1024*6, resource.DecimalSI), + }, + } + + rs.AddToResourceSummary(crn1) + rs.AddToResourceSummary(crn2) + rs.AddToResourceSummary(crn3) + + for index := range rs { + num := rs.GetNodeNumFromModel(&rs[index]) + if index == 0 && num != 0 { + t.Errorf("Got %v expected %v", num, 0) + } + if index == 1 && num != 2 { + t.Errorf("Got %v expected %v", num, 2) + } + if index == 0 && rs[index].Quantity != 0 { + t.Errorf("Got %v expected %v", rs[index].Quantity, 0) + } + if index == 1 && rs[index].Quantity != 6 { + t.Errorf("Got %v expected %v", rs[index].Quantity, 6) + } + } + + crn2 = ClusterResourceNode{ + quantity: 1, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(1, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(1024*6, resource.DecimalSI), + }, + } + + crn4 := ClusterResourceNode{ + quantity: 2, + resourceList: ResourceList{ + clusterapis.ResourceCPU: *resource.NewMilliQuantity(1, resource.DecimalSI), + clusterapis.ResourceMemory: *resource.NewQuantity(19, resource.DecimalSI), + }, + } + + err = rs.UpdateInResourceSummary(crn2, crn4) + + if err != nil { + t.Errorf("Got %v expected %v", err, nil) + } + + if actualValue := rs[1]; actualValue.Quantity != 7 { + t.Errorf("Got %v expected %v", actualValue, 7) + } + + if actualValue := rs[1]; rs.GetNodeNumFromModel(&actualValue) != 3 { + t.Errorf("Got %v expected %v", rs.GetNodeNumFromModel(&actualValue), 3) + } +} diff --git a/vendor/github.com/emirpasic/gods/LICENSE b/vendor/github.com/emirpasic/gods/LICENSE new file mode 100644 index 000000000..e5e449b6e --- /dev/null +++ b/vendor/github.com/emirpasic/gods/LICENSE @@ -0,0 +1,41 @@ +Copyright (c) 2015, Emir Pasic +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------- + +AVL Tree: + +Copyright (c) 2017 Benjamin Scher Purcell + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/vendor/github.com/emirpasic/gods/containers/containers.go b/vendor/github.com/emirpasic/gods/containers/containers.go new file mode 100644 index 000000000..a512a3cba --- /dev/null +++ b/vendor/github.com/emirpasic/gods/containers/containers.go @@ -0,0 +1,36 @@ +// Copyright (c) 2015, Emir Pasic. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package containers provides core interfaces and functions for data structures. +// +// Container is the base interface for all data structures to implement. +// +// Iterators provide stateful iterators. +// +// Enumerable provides Ruby inspired (each, select, map, find, any?, etc.) container functions. +// +// Serialization provides serializers (marshalers) and deserializers (unmarshalers). +package containers + +import "github.com/emirpasic/gods/utils" + +// Container is base interface that all data structures implement. +type Container interface { + Empty() bool + Size() int + Clear() + Values() []interface{} + String() string +} + +// GetSortedValues returns sorted container's elements with respect to the passed comparator. +// Does not affect the ordering of elements within the container. +func GetSortedValues(container Container, comparator utils.Comparator) []interface{} { + values := container.Values() + if len(values) < 2 { + return values + } + utils.Sort(values, comparator) + return values +} diff --git a/vendor/github.com/emirpasic/gods/containers/enumerable.go b/vendor/github.com/emirpasic/gods/containers/enumerable.go new file mode 100644 index 000000000..70660054a --- /dev/null +++ b/vendor/github.com/emirpasic/gods/containers/enumerable.go @@ -0,0 +1,57 @@ +// Copyright (c) 2015, Emir Pasic. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package containers + +// EnumerableWithIndex provides functions for ordered containers whose values can be fetched by an index. +type EnumerableWithIndex interface { + // Each calls the given function once for each element, passing that element's index and value. + Each(func(index int, value interface{})) + + // Map invokes the given function once for each element and returns a + // container containing the values returned by the given function. + // Map(func(index int, value interface{}) interface{}) Container + + // Select returns a new container containing all elements for which the given function returns a true value. + // Select(func(index int, value interface{}) bool) Container + + // Any passes each element of the container to the given function and + // returns true if the function ever returns true for any element. + Any(func(index int, value interface{}) bool) bool + + // All passes each element of the container to the given function and + // returns true if the function returns true for all elements. + All(func(index int, value interface{}) bool) bool + + // Find passes each element of the container to the given function and returns + // the first (index,value) for which the function is true or -1,nil otherwise + // if no element matches the criteria. + Find(func(index int, value interface{}) bool) (int, interface{}) +} + +// EnumerableWithKey provides functions for ordered containers whose values whose elements are key/value pairs. +type EnumerableWithKey interface { + // Each calls the given function once for each element, passing that element's key and value. + Each(func(key interface{}, value interface{})) + + // Map invokes the given function once for each element and returns a container + // containing the values returned by the given function as key/value pairs. + // Map(func(key interface{}, value interface{}) (interface{}, interface{})) Container + + // Select returns a new container containing all elements for which the given function returns a true value. + // Select(func(key interface{}, value interface{}) bool) Container + + // Any passes each element of the container to the given function and + // returns true if the function ever returns true for any element. + Any(func(key interface{}, value interface{}) bool) bool + + // All passes each element of the container to the given function and + // returns true if the function returns true for all elements. + All(func(key interface{}, value interface{}) bool) bool + + // Find passes each element of the container to the given function and returns + // the first (key,value) for which the function is true or nil,nil otherwise if no element + // matches the criteria. + Find(func(key interface{}, value interface{}) bool) (interface{}, interface{}) +} diff --git a/vendor/github.com/emirpasic/gods/containers/iterator.go b/vendor/github.com/emirpasic/gods/containers/iterator.go new file mode 100644 index 000000000..73994ec82 --- /dev/null +++ b/vendor/github.com/emirpasic/gods/containers/iterator.go @@ -0,0 +1,133 @@ +// Copyright (c) 2015, Emir Pasic. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package containers + +// IteratorWithIndex is stateful iterator for ordered containers whose values can be fetched by an index. +type IteratorWithIndex interface { + // Next moves the iterator to the next element and returns true if there was a next element in the container. + // If Next() returns true, then next element's index and value can be retrieved by Index() and Value(). + // If Next() was called for the first time, then it will point the iterator to the first element if it exists. + // Modifies the state of the iterator. + Next() bool + + // Value returns the current element's value. + // Does not modify the state of the iterator. + Value() interface{} + + // Index returns the current element's index. + // Does not modify the state of the iterator. + Index() int + + // Begin resets the iterator to its initial state (one-before-first) + // Call Next() to fetch the first element if any. + Begin() + + // First moves the iterator to the first element and returns true if there was a first element in the container. + // If First() returns true, then first element's index and value can be retrieved by Index() and Value(). + // Modifies the state of the iterator. + First() bool + + // NextTo moves the iterator to the next element from current position that satisfies the condition given by the + // passed function, and returns true if there was a next element in the container. + // If NextTo() returns true, then next element's index and value can be retrieved by Index() and Value(). + // Modifies the state of the iterator. + NextTo(func(index int, value interface{}) bool) bool +} + +// IteratorWithKey is a stateful iterator for ordered containers whose elements are key value pairs. +type IteratorWithKey interface { + // Next moves the iterator to the next element and returns true if there was a next element in the container. + // If Next() returns true, then next element's key and value can be retrieved by Key() and Value(). + // If Next() was called for the first time, then it will point the iterator to the first element if it exists. + // Modifies the state of the iterator. + Next() bool + + // Value returns the current element's value. + // Does not modify the state of the iterator. + Value() interface{} + + // Key returns the current element's key. + // Does not modify the state of the iterator. + Key() interface{} + + // Begin resets the iterator to its initial state (one-before-first) + // Call Next() to fetch the first element if any. + Begin() + + // First moves the iterator to the first element and returns true if there was a first element in the container. + // If First() returns true, then first element's key and value can be retrieved by Key() and Value(). + // Modifies the state of the iterator. + First() bool + + // NextTo moves the iterator to the next element from current position that satisfies the condition given by the + // passed function, and returns true if there was a next element in the container. + // If NextTo() returns true, then next element's key and value can be retrieved by Key() and Value(). + // Modifies the state of the iterator. + NextTo(func(key interface{}, value interface{}) bool) bool +} + +// ReverseIteratorWithIndex is stateful iterator for ordered containers whose values can be fetched by an index. +// +// Essentially it is the same as IteratorWithIndex, but provides additional: +// +// Prev() function to enable traversal in reverse +// +// Last() function to move the iterator to the last element. +// +// End() function to move the iterator past the last element (one-past-the-end). +type ReverseIteratorWithIndex interface { + // Prev moves the iterator to the previous element and returns true if there was a previous element in the container. + // If Prev() returns true, then previous element's index and value can be retrieved by Index() and Value(). + // Modifies the state of the iterator. + Prev() bool + + // End moves the iterator past the last element (one-past-the-end). + // Call Prev() to fetch the last element if any. + End() + + // Last moves the iterator to the last element and returns true if there was a last element in the container. + // If Last() returns true, then last element's index and value can be retrieved by Index() and Value(). + // Modifies the state of the iterator. + Last() bool + + // PrevTo moves the iterator to the previous element from current position that satisfies the condition given by the + // passed function, and returns true if there was a next element in the container. + // If PrevTo() returns true, then next element's index and value can be retrieved by Index() and Value(). + // Modifies the state of the iterator. + PrevTo(func(index int, value interface{}) bool) bool + + IteratorWithIndex +} + +// ReverseIteratorWithKey is a stateful iterator for ordered containers whose elements are key value pairs. +// +// Essentially it is the same as IteratorWithKey, but provides additional: +// +// Prev() function to enable traversal in reverse +// +// Last() function to move the iterator to the last element. +type ReverseIteratorWithKey interface { + // Prev moves the iterator to the previous element and returns true if there was a previous element in the container. + // If Prev() returns true, then previous element's key and value can be retrieved by Key() and Value(). + // Modifies the state of the iterator. + Prev() bool + + // End moves the iterator past the last element (one-past-the-end). + // Call Prev() to fetch the last element if any. + End() + + // Last moves the iterator to the last element and returns true if there was a last element in the container. + // If Last() returns true, then last element's key and value can be retrieved by Key() and Value(). + // Modifies the state of the iterator. + Last() bool + + // PrevTo moves the iterator to the previous element from current position that satisfies the condition given by the + // passed function, and returns true if there was a next element in the container. + // If PrevTo() returns true, then next element's key and value can be retrieved by Key() and Value(). + // Modifies the state of the iterator. + PrevTo(func(key interface{}, value interface{}) bool) bool + + IteratorWithKey +} diff --git a/vendor/github.com/emirpasic/gods/containers/serialization.go b/vendor/github.com/emirpasic/gods/containers/serialization.go new file mode 100644 index 000000000..fd9cbe23a --- /dev/null +++ b/vendor/github.com/emirpasic/gods/containers/serialization.go @@ -0,0 +1,21 @@ +// Copyright (c) 2015, Emir Pasic. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package containers + +// JSONSerializer provides JSON serialization +type JSONSerializer interface { + // ToJSON outputs the JSON representation of containers's elements. + ToJSON() ([]byte, error) + // MarshalJSON @implements json.Marshaler + MarshalJSON() ([]byte, error) +} + +// JSONDeserializer provides JSON deserialization +type JSONDeserializer interface { + // FromJSON populates containers's elements from the input JSON representation. + FromJSON([]byte) error + // UnmarshalJSON @implements json.Unmarshaler + UnmarshalJSON([]byte) error +} diff --git a/vendor/github.com/emirpasic/gods/trees/redblacktree/iterator.go b/vendor/github.com/emirpasic/gods/trees/redblacktree/iterator.go new file mode 100644 index 000000000..e39da7d4b --- /dev/null +++ b/vendor/github.com/emirpasic/gods/trees/redblacktree/iterator.go @@ -0,0 +1,190 @@ +// Copyright (c) 2015, Emir Pasic. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package redblacktree + +import "github.com/emirpasic/gods/containers" + +// Assert Iterator implementation +var _ containers.ReverseIteratorWithKey = (*Iterator)(nil) + +// Iterator holding the iterator's state +type Iterator struct { + tree *Tree + node *Node + position position +} + +type position byte + +const ( + begin, between, end position = 0, 1, 2 +) + +// Iterator returns a stateful iterator whose elements are key/value pairs. +func (tree *Tree) Iterator() Iterator { + return Iterator{tree: tree, node: nil, position: begin} +} + +// IteratorAt returns a stateful iterator whose elements are key/value pairs that is initialised at a particular node. +func (tree *Tree) IteratorAt(node *Node) Iterator { + return Iterator{tree: tree, node: node, position: between} +} + +// Next moves the iterator to the next element and returns true if there was a next element in the container. +// If Next() returns true, then next element's key and value can be retrieved by Key() and Value(). +// If Next() was called for the first time, then it will point the iterator to the first element if it exists. +// Modifies the state of the iterator. +func (iterator *Iterator) Next() bool { + if iterator.position == end { + goto end + } + if iterator.position == begin { + left := iterator.tree.Left() + if left == nil { + goto end + } + iterator.node = left + goto between + } + if iterator.node.Right != nil { + iterator.node = iterator.node.Right + for iterator.node.Left != nil { + iterator.node = iterator.node.Left + } + goto between + } + for iterator.node.Parent != nil { + node := iterator.node + iterator.node = iterator.node.Parent + if node == iterator.node.Left { + goto between + } + } + +end: + iterator.node = nil + iterator.position = end + return false + +between: + iterator.position = between + return true +} + +// Prev moves the iterator to the previous element and returns true if there was a previous element in the container. +// If Prev() returns true, then previous element's key and value can be retrieved by Key() and Value(). +// Modifies the state of the iterator. +func (iterator *Iterator) Prev() bool { + if iterator.position == begin { + goto begin + } + if iterator.position == end { + right := iterator.tree.Right() + if right == nil { + goto begin + } + iterator.node = right + goto between + } + if iterator.node.Left != nil { + iterator.node = iterator.node.Left + for iterator.node.Right != nil { + iterator.node = iterator.node.Right + } + goto between + } + for iterator.node.Parent != nil { + node := iterator.node + iterator.node = iterator.node.Parent + if node == iterator.node.Right { + goto between + } + } + +begin: + iterator.node = nil + iterator.position = begin + return false + +between: + iterator.position = between + return true +} + +// Value returns the current element's value. +// Does not modify the state of the iterator. +func (iterator *Iterator) Value() interface{} { + return iterator.node.Value +} + +// Key returns the current element's key. +// Does not modify the state of the iterator. +func (iterator *Iterator) Key() interface{} { + return iterator.node.Key +} + +// Node returns the current element's node. +// Does not modify the state of the iterator. +func (iterator *Iterator) Node() *Node { + return iterator.node +} + +// Begin resets the iterator to its initial state (one-before-first) +// Call Next() to fetch the first element if any. +func (iterator *Iterator) Begin() { + iterator.node = nil + iterator.position = begin +} + +// End moves the iterator past the last element (one-past-the-end). +// Call Prev() to fetch the last element if any. +func (iterator *Iterator) End() { + iterator.node = nil + iterator.position = end +} + +// First moves the iterator to the first element and returns true if there was a first element in the container. +// If First() returns true, then first element's key and value can be retrieved by Key() and Value(). +// Modifies the state of the iterator +func (iterator *Iterator) First() bool { + iterator.Begin() + return iterator.Next() +} + +// Last moves the iterator to the last element and returns true if there was a last element in the container. +// If Last() returns true, then last element's key and value can be retrieved by Key() and Value(). +// Modifies the state of the iterator. +func (iterator *Iterator) Last() bool { + iterator.End() + return iterator.Prev() +} + +// NextTo moves the iterator to the next element from current position that satisfies the condition given by the +// passed function, and returns true if there was a next element in the container. +// If NextTo() returns true, then next element's key and value can be retrieved by Key() and Value(). +// Modifies the state of the iterator. +func (iterator *Iterator) NextTo(f func(key interface{}, value interface{}) bool) bool { + for iterator.Next() { + key, value := iterator.Key(), iterator.Value() + if f(key, value) { + return true + } + } + return false +} + +// PrevTo moves the iterator to the previous element from current position that satisfies the condition given by the +// passed function, and returns true if there was a next element in the container. +// If PrevTo() returns true, then next element's key and value can be retrieved by Key() and Value(). +// Modifies the state of the iterator. +func (iterator *Iterator) PrevTo(f func(key interface{}, value interface{}) bool) bool { + for iterator.Prev() { + key, value := iterator.Key(), iterator.Value() + if f(key, value) { + return true + } + } + return false +} diff --git a/vendor/github.com/emirpasic/gods/trees/redblacktree/redblacktree.go b/vendor/github.com/emirpasic/gods/trees/redblacktree/redblacktree.go new file mode 100644 index 000000000..b335e3df1 --- /dev/null +++ b/vendor/github.com/emirpasic/gods/trees/redblacktree/redblacktree.go @@ -0,0 +1,548 @@ +// Copyright (c) 2015, Emir Pasic. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package redblacktree implements a red-black tree. +// +// Used by TreeSet and TreeMap. +// +// Structure is not thread safe. +// +// References: http://en.wikipedia.org/wiki/Red%E2%80%93black_tree +package redblacktree + +import ( + "fmt" + "github.com/emirpasic/gods/trees" + "github.com/emirpasic/gods/utils" +) + +// Assert Tree implementation +var _ trees.Tree = (*Tree)(nil) + +type color bool + +const ( + black, red color = true, false +) + +// Tree holds elements of the red-black tree +type Tree struct { + Root *Node + size int + Comparator utils.Comparator +} + +// Node is a single element within the tree +type Node struct { + Key interface{} + Value interface{} + color color + Left *Node + Right *Node + Parent *Node +} + +// NewWith instantiates a red-black tree with the custom comparator. +func NewWith(comparator utils.Comparator) *Tree { + return &Tree{Comparator: comparator} +} + +// NewWithIntComparator instantiates a red-black tree with the IntComparator, i.e. keys are of type int. +func NewWithIntComparator() *Tree { + return &Tree{Comparator: utils.IntComparator} +} + +// NewWithStringComparator instantiates a red-black tree with the StringComparator, i.e. keys are of type string. +func NewWithStringComparator() *Tree { + return &Tree{Comparator: utils.StringComparator} +} + +// Put inserts node into the tree. +// Key should adhere to the comparator's type assertion, otherwise method panics. +func (tree *Tree) Put(key interface{}, value interface{}) { + var insertedNode *Node + if tree.Root == nil { + // Assert key is of comparator's type for initial tree + tree.Comparator(key, key) + tree.Root = &Node{Key: key, Value: value, color: red} + insertedNode = tree.Root + } else { + node := tree.Root + loop := true + for loop { + compare := tree.Comparator(key, node.Key) + switch { + case compare == 0: + node.Key = key + node.Value = value + return + case compare < 0: + if node.Left == nil { + node.Left = &Node{Key: key, Value: value, color: red} + insertedNode = node.Left + loop = false + } else { + node = node.Left + } + case compare > 0: + if node.Right == nil { + node.Right = &Node{Key: key, Value: value, color: red} + insertedNode = node.Right + loop = false + } else { + node = node.Right + } + } + } + insertedNode.Parent = node + } + tree.insertCase1(insertedNode) + tree.size++ +} + +// Get searches the node in the tree by key and returns its value or nil if key is not found in tree. +// Second return parameter is true if key was found, otherwise false. +// Key should adhere to the comparator's type assertion, otherwise method panics. +func (tree *Tree) Get(key interface{}) (value interface{}, found bool) { + node := tree.lookup(key) + if node != nil { + return node.Value, true + } + return nil, false +} + +// GetNode searches the node in the tree by key and returns its node or nil if key is not found in tree. +// Key should adhere to the comparator's type assertion, otherwise method panics. +func (tree *Tree) GetNode(key interface{}) *Node { + return tree.lookup(key) +} + +// Remove remove the node from the tree by key. +// Key should adhere to the comparator's type assertion, otherwise method panics. +func (tree *Tree) Remove(key interface{}) { + var child *Node + node := tree.lookup(key) + if node == nil { + return + } + if node.Left != nil && node.Right != nil { + pred := node.Left.maximumNode() + node.Key = pred.Key + node.Value = pred.Value + node = pred + } + if node.Left == nil || node.Right == nil { + if node.Right == nil { + child = node.Left + } else { + child = node.Right + } + if node.color == black { + node.color = nodeColor(child) + tree.deleteCase1(node) + } + tree.replaceNode(node, child) + if node.Parent == nil && child != nil { + child.color = black + } + } + tree.size-- +} + +// Empty returns true if tree does not contain any nodes +func (tree *Tree) Empty() bool { + return tree.size == 0 +} + +// Size returns number of nodes in the tree. +func (tree *Tree) Size() int { + return tree.size +} + +// Size returns the number of elements stored in the subtree. +// Computed dynamically on each call, i.e. the subtree is traversed to count the number of the nodes. +func (node *Node) Size() int { + if node == nil { + return 0 + } + size := 1 + if node.Left != nil { + size += node.Left.Size() + } + if node.Right != nil { + size += node.Right.Size() + } + return size +} + +// Keys returns all keys in-order +func (tree *Tree) Keys() []interface{} { + keys := make([]interface{}, tree.size) + it := tree.Iterator() + for i := 0; it.Next(); i++ { + keys[i] = it.Key() + } + return keys +} + +// Values returns all values in-order based on the key. +func (tree *Tree) Values() []interface{} { + values := make([]interface{}, tree.size) + it := tree.Iterator() + for i := 0; it.Next(); i++ { + values[i] = it.Value() + } + return values +} + +// Left returns the left-most (min) node or nil if tree is empty. +func (tree *Tree) Left() *Node { + var parent *Node + current := tree.Root + for current != nil { + parent = current + current = current.Left + } + return parent +} + +// Right returns the right-most (max) node or nil if tree is empty. +func (tree *Tree) Right() *Node { + var parent *Node + current := tree.Root + for current != nil { + parent = current + current = current.Right + } + return parent +} + +// Floor Finds floor node of the input key, return the floor node or nil if no floor is found. +// Second return parameter is true if floor was found, otherwise false. +// +// Floor node is defined as the largest node that is smaller than or equal to the given node. +// A floor node may not be found, either because the tree is empty, or because +// all nodes in the tree are larger than the given node. +// +// Key should adhere to the comparator's type assertion, otherwise method panics. +func (tree *Tree) Floor(key interface{}) (floor *Node, found bool) { + found = false + node := tree.Root + for node != nil { + compare := tree.Comparator(key, node.Key) + switch { + case compare == 0: + return node, true + case compare < 0: + node = node.Left + case compare > 0: + floor, found = node, true + node = node.Right + } + } + if found { + return floor, true + } + return nil, false +} + +// Ceiling finds ceiling node of the input key, return the ceiling node or nil if no ceiling is found. +// Second return parameter is true if ceiling was found, otherwise false. +// +// Ceiling node is defined as the smallest node that is larger than or equal to the given node. +// A ceiling node may not be found, either because the tree is empty, or because +// all nodes in the tree are smaller than the given node. +// +// Key should adhere to the comparator's type assertion, otherwise method panics. +func (tree *Tree) Ceiling(key interface{}) (ceiling *Node, found bool) { + found = false + node := tree.Root + for node != nil { + compare := tree.Comparator(key, node.Key) + switch { + case compare == 0: + return node, true + case compare < 0: + ceiling, found = node, true + node = node.Left + case compare > 0: + node = node.Right + } + } + if found { + return ceiling, true + } + return nil, false +} + +// Clear removes all nodes from the tree. +func (tree *Tree) Clear() { + tree.Root = nil + tree.size = 0 +} + +// String returns a string representation of container +func (tree *Tree) String() string { + str := "RedBlackTree\n" + if !tree.Empty() { + output(tree.Root, "", true, &str) + } + return str +} + +func (node *Node) String() string { + return fmt.Sprintf("%v", node.Key) +} + +func output(node *Node, prefix string, isTail bool, str *string) { + if node.Right != nil { + newPrefix := prefix + if isTail { + newPrefix += "│ " + } else { + newPrefix += " " + } + output(node.Right, newPrefix, false, str) + } + *str += prefix + if isTail { + *str += "└── " + } else { + *str += "┌── " + } + *str += node.String() + "\n" + if node.Left != nil { + newPrefix := prefix + if isTail { + newPrefix += " " + } else { + newPrefix += "│ " + } + output(node.Left, newPrefix, true, str) + } +} + +func (tree *Tree) lookup(key interface{}) *Node { + node := tree.Root + for node != nil { + compare := tree.Comparator(key, node.Key) + switch { + case compare == 0: + return node + case compare < 0: + node = node.Left + case compare > 0: + node = node.Right + } + } + return nil +} + +func (node *Node) grandparent() *Node { + if node != nil && node.Parent != nil { + return node.Parent.Parent + } + return nil +} + +func (node *Node) uncle() *Node { + if node == nil || node.Parent == nil || node.Parent.Parent == nil { + return nil + } + return node.Parent.sibling() +} + +func (node *Node) sibling() *Node { + if node == nil || node.Parent == nil { + return nil + } + if node == node.Parent.Left { + return node.Parent.Right + } + return node.Parent.Left +} + +func (tree *Tree) rotateLeft(node *Node) { + right := node.Right + tree.replaceNode(node, right) + node.Right = right.Left + if right.Left != nil { + right.Left.Parent = node + } + right.Left = node + node.Parent = right +} + +func (tree *Tree) rotateRight(node *Node) { + left := node.Left + tree.replaceNode(node, left) + node.Left = left.Right + if left.Right != nil { + left.Right.Parent = node + } + left.Right = node + node.Parent = left +} + +func (tree *Tree) replaceNode(old *Node, new *Node) { + if old.Parent == nil { + tree.Root = new + } else { + if old == old.Parent.Left { + old.Parent.Left = new + } else { + old.Parent.Right = new + } + } + if new != nil { + new.Parent = old.Parent + } +} + +func (tree *Tree) insertCase1(node *Node) { + if node.Parent == nil { + node.color = black + } else { + tree.insertCase2(node) + } +} + +func (tree *Tree) insertCase2(node *Node) { + if nodeColor(node.Parent) == black { + return + } + tree.insertCase3(node) +} + +func (tree *Tree) insertCase3(node *Node) { + uncle := node.uncle() + if nodeColor(uncle) == red { + node.Parent.color = black + uncle.color = black + node.grandparent().color = red + tree.insertCase1(node.grandparent()) + } else { + tree.insertCase4(node) + } +} + +func (tree *Tree) insertCase4(node *Node) { + grandparent := node.grandparent() + if node == node.Parent.Right && node.Parent == grandparent.Left { + tree.rotateLeft(node.Parent) + node = node.Left + } else if node == node.Parent.Left && node.Parent == grandparent.Right { + tree.rotateRight(node.Parent) + node = node.Right + } + tree.insertCase5(node) +} + +func (tree *Tree) insertCase5(node *Node) { + node.Parent.color = black + grandparent := node.grandparent() + grandparent.color = red + if node == node.Parent.Left && node.Parent == grandparent.Left { + tree.rotateRight(grandparent) + } else if node == node.Parent.Right && node.Parent == grandparent.Right { + tree.rotateLeft(grandparent) + } +} + +func (node *Node) maximumNode() *Node { + if node == nil { + return nil + } + for node.Right != nil { + node = node.Right + } + return node +} + +func (tree *Tree) deleteCase1(node *Node) { + if node.Parent == nil { + return + } + tree.deleteCase2(node) +} + +func (tree *Tree) deleteCase2(node *Node) { + sibling := node.sibling() + if nodeColor(sibling) == red { + node.Parent.color = red + sibling.color = black + if node == node.Parent.Left { + tree.rotateLeft(node.Parent) + } else { + tree.rotateRight(node.Parent) + } + } + tree.deleteCase3(node) +} + +func (tree *Tree) deleteCase3(node *Node) { + sibling := node.sibling() + if nodeColor(node.Parent) == black && + nodeColor(sibling) == black && + nodeColor(sibling.Left) == black && + nodeColor(sibling.Right) == black { + sibling.color = red + tree.deleteCase1(node.Parent) + } else { + tree.deleteCase4(node) + } +} + +func (tree *Tree) deleteCase4(node *Node) { + sibling := node.sibling() + if nodeColor(node.Parent) == red && + nodeColor(sibling) == black && + nodeColor(sibling.Left) == black && + nodeColor(sibling.Right) == black { + sibling.color = red + node.Parent.color = black + } else { + tree.deleteCase5(node) + } +} + +func (tree *Tree) deleteCase5(node *Node) { + sibling := node.sibling() + if node == node.Parent.Left && + nodeColor(sibling) == black && + nodeColor(sibling.Left) == red && + nodeColor(sibling.Right) == black { + sibling.color = red + sibling.Left.color = black + tree.rotateRight(sibling) + } else if node == node.Parent.Right && + nodeColor(sibling) == black && + nodeColor(sibling.Right) == red && + nodeColor(sibling.Left) == black { + sibling.color = red + sibling.Right.color = black + tree.rotateLeft(sibling) + } + tree.deleteCase6(node) +} + +func (tree *Tree) deleteCase6(node *Node) { + sibling := node.sibling() + sibling.color = nodeColor(node.Parent) + node.Parent.color = black + if node == node.Parent.Left && nodeColor(sibling.Right) == red { + sibling.Right.color = black + tree.rotateLeft(node.Parent) + } else if nodeColor(sibling.Left) == red { + sibling.Left.color = black + tree.rotateRight(node.Parent) + } +} + +func nodeColor(node *Node) color { + if node == nil { + return black + } + return node.color +} diff --git a/vendor/github.com/emirpasic/gods/trees/redblacktree/serialization.go b/vendor/github.com/emirpasic/gods/trees/redblacktree/serialization.go new file mode 100644 index 000000000..9f2a23c00 --- /dev/null +++ b/vendor/github.com/emirpasic/gods/trees/redblacktree/serialization.go @@ -0,0 +1,48 @@ +// Copyright (c) 2015, Emir Pasic. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package redblacktree + +import ( + "encoding/json" + "github.com/emirpasic/gods/containers" + "github.com/emirpasic/gods/utils" +) + +// Assert Serialization implementation +var _ containers.JSONSerializer = (*Tree)(nil) +var _ containers.JSONDeserializer = (*Tree)(nil) + +// ToJSON outputs the JSON representation of the tree. +func (tree *Tree) ToJSON() ([]byte, error) { + elements := make(map[string]interface{}) + it := tree.Iterator() + for it.Next() { + elements[utils.ToString(it.Key())] = it.Value() + } + return json.Marshal(&elements) +} + +// FromJSON populates the tree from the input JSON representation. +func (tree *Tree) FromJSON(data []byte) error { + elements := make(map[string]interface{}) + err := json.Unmarshal(data, &elements) + if err == nil { + tree.Clear() + for key, value := range elements { + tree.Put(key, value) + } + } + return err +} + +// UnmarshalJSON @implements json.Unmarshaler +func (tree *Tree) UnmarshalJSON(bytes []byte) error { + return tree.FromJSON(bytes) +} + +// MarshalJSON @implements json.Marshaler +func (tree *Tree) MarshalJSON() ([]byte, error) { + return tree.ToJSON() +} diff --git a/vendor/github.com/emirpasic/gods/trees/trees.go b/vendor/github.com/emirpasic/gods/trees/trees.go new file mode 100644 index 000000000..8d1b868f1 --- /dev/null +++ b/vendor/github.com/emirpasic/gods/trees/trees.go @@ -0,0 +1,22 @@ +// Copyright (c) 2015, Emir Pasic. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package trees provides an abstract Tree interface. +// +// In computer science, a tree is a widely used abstract data type (ADT) or data structure implementing this ADT that simulates a hierarchical tree structure, with a root value and subtrees of children with a parent node, represented as a set of linked nodes. +// +// Reference: https://en.wikipedia.org/wiki/Tree_%28data_structure%29 +package trees + +import "github.com/emirpasic/gods/containers" + +// Tree interface that all trees implement +type Tree interface { + containers.Container + // Empty() bool + // Size() int + // Clear() + // Values() []interface{} + // String() string +} diff --git a/vendor/github.com/emirpasic/gods/utils/comparator.go b/vendor/github.com/emirpasic/gods/utils/comparator.go new file mode 100644 index 000000000..6a9afbf34 --- /dev/null +++ b/vendor/github.com/emirpasic/gods/utils/comparator.go @@ -0,0 +1,251 @@ +// Copyright (c) 2015, Emir Pasic. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package utils + +import "time" + +// Comparator will make type assertion (see IntComparator for example), +// which will panic if a or b are not of the asserted type. +// +// Should return a number: +// negative , if a < b +// zero , if a == b +// positive , if a > b +type Comparator func(a, b interface{}) int + +// StringComparator provides a fast comparison on strings +func StringComparator(a, b interface{}) int { + s1 := a.(string) + s2 := b.(string) + min := len(s2) + if len(s1) < len(s2) { + min = len(s1) + } + diff := 0 + for i := 0; i < min && diff == 0; i++ { + diff = int(s1[i]) - int(s2[i]) + } + if diff == 0 { + diff = len(s1) - len(s2) + } + if diff < 0 { + return -1 + } + if diff > 0 { + return 1 + } + return 0 +} + +// IntComparator provides a basic comparison on int +func IntComparator(a, b interface{}) int { + aAsserted := a.(int) + bAsserted := b.(int) + switch { + case aAsserted > bAsserted: + return 1 + case aAsserted < bAsserted: + return -1 + default: + return 0 + } +} + +// Int8Comparator provides a basic comparison on int8 +func Int8Comparator(a, b interface{}) int { + aAsserted := a.(int8) + bAsserted := b.(int8) + switch { + case aAsserted > bAsserted: + return 1 + case aAsserted < bAsserted: + return -1 + default: + return 0 + } +} + +// Int16Comparator provides a basic comparison on int16 +func Int16Comparator(a, b interface{}) int { + aAsserted := a.(int16) + bAsserted := b.(int16) + switch { + case aAsserted > bAsserted: + return 1 + case aAsserted < bAsserted: + return -1 + default: + return 0 + } +} + +// Int32Comparator provides a basic comparison on int32 +func Int32Comparator(a, b interface{}) int { + aAsserted := a.(int32) + bAsserted := b.(int32) + switch { + case aAsserted > bAsserted: + return 1 + case aAsserted < bAsserted: + return -1 + default: + return 0 + } +} + +// Int64Comparator provides a basic comparison on int64 +func Int64Comparator(a, b interface{}) int { + aAsserted := a.(int64) + bAsserted := b.(int64) + switch { + case aAsserted > bAsserted: + return 1 + case aAsserted < bAsserted: + return -1 + default: + return 0 + } +} + +// UIntComparator provides a basic comparison on uint +func UIntComparator(a, b interface{}) int { + aAsserted := a.(uint) + bAsserted := b.(uint) + switch { + case aAsserted > bAsserted: + return 1 + case aAsserted < bAsserted: + return -1 + default: + return 0 + } +} + +// UInt8Comparator provides a basic comparison on uint8 +func UInt8Comparator(a, b interface{}) int { + aAsserted := a.(uint8) + bAsserted := b.(uint8) + switch { + case aAsserted > bAsserted: + return 1 + case aAsserted < bAsserted: + return -1 + default: + return 0 + } +} + +// UInt16Comparator provides a basic comparison on uint16 +func UInt16Comparator(a, b interface{}) int { + aAsserted := a.(uint16) + bAsserted := b.(uint16) + switch { + case aAsserted > bAsserted: + return 1 + case aAsserted < bAsserted: + return -1 + default: + return 0 + } +} + +// UInt32Comparator provides a basic comparison on uint32 +func UInt32Comparator(a, b interface{}) int { + aAsserted := a.(uint32) + bAsserted := b.(uint32) + switch { + case aAsserted > bAsserted: + return 1 + case aAsserted < bAsserted: + return -1 + default: + return 0 + } +} + +// UInt64Comparator provides a basic comparison on uint64 +func UInt64Comparator(a, b interface{}) int { + aAsserted := a.(uint64) + bAsserted := b.(uint64) + switch { + case aAsserted > bAsserted: + return 1 + case aAsserted < bAsserted: + return -1 + default: + return 0 + } +} + +// Float32Comparator provides a basic comparison on float32 +func Float32Comparator(a, b interface{}) int { + aAsserted := a.(float32) + bAsserted := b.(float32) + switch { + case aAsserted > bAsserted: + return 1 + case aAsserted < bAsserted: + return -1 + default: + return 0 + } +} + +// Float64Comparator provides a basic comparison on float64 +func Float64Comparator(a, b interface{}) int { + aAsserted := a.(float64) + bAsserted := b.(float64) + switch { + case aAsserted > bAsserted: + return 1 + case aAsserted < bAsserted: + return -1 + default: + return 0 + } +} + +// ByteComparator provides a basic comparison on byte +func ByteComparator(a, b interface{}) int { + aAsserted := a.(byte) + bAsserted := b.(byte) + switch { + case aAsserted > bAsserted: + return 1 + case aAsserted < bAsserted: + return -1 + default: + return 0 + } +} + +// RuneComparator provides a basic comparison on rune +func RuneComparator(a, b interface{}) int { + aAsserted := a.(rune) + bAsserted := b.(rune) + switch { + case aAsserted > bAsserted: + return 1 + case aAsserted < bAsserted: + return -1 + default: + return 0 + } +} + +// TimeComparator provides a basic comparison on time.Time +func TimeComparator(a, b interface{}) int { + aAsserted := a.(time.Time) + bAsserted := b.(time.Time) + + switch { + case aAsserted.After(bAsserted): + return 1 + case aAsserted.Before(bAsserted): + return -1 + default: + return 0 + } +} diff --git a/vendor/github.com/emirpasic/gods/utils/sort.go b/vendor/github.com/emirpasic/gods/utils/sort.go new file mode 100644 index 000000000..79ced1f5d --- /dev/null +++ b/vendor/github.com/emirpasic/gods/utils/sort.go @@ -0,0 +1,29 @@ +// Copyright (c) 2015, Emir Pasic. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package utils + +import "sort" + +// Sort sorts values (in-place) with respect to the given comparator. +// +// Uses Go's sort (hybrid of quicksort for large and then insertion sort for smaller slices). +func Sort(values []interface{}, comparator Comparator) { + sort.Sort(sortable{values, comparator}) +} + +type sortable struct { + values []interface{} + comparator Comparator +} + +func (s sortable) Len() int { + return len(s.values) +} +func (s sortable) Swap(i, j int) { + s.values[i], s.values[j] = s.values[j], s.values[i] +} +func (s sortable) Less(i, j int) bool { + return s.comparator(s.values[i], s.values[j]) < 0 +} diff --git a/vendor/github.com/emirpasic/gods/utils/utils.go b/vendor/github.com/emirpasic/gods/utils/utils.go new file mode 100644 index 000000000..262c62576 --- /dev/null +++ b/vendor/github.com/emirpasic/gods/utils/utils.go @@ -0,0 +1,47 @@ +// Copyright (c) 2015, Emir Pasic. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package utils provides common utility functions. +// +// Provided functionalities: +// - sorting +// - comparators +package utils + +import ( + "fmt" + "strconv" +) + +// ToString converts a value to string. +func ToString(value interface{}) string { + switch value := value.(type) { + case string: + return value + case int8: + return strconv.FormatInt(int64(value), 10) + case int16: + return strconv.FormatInt(int64(value), 10) + case int32: + return strconv.FormatInt(int64(value), 10) + case int64: + return strconv.FormatInt(value, 10) + case uint8: + return strconv.FormatUint(uint64(value), 10) + case uint16: + return strconv.FormatUint(uint64(value), 10) + case uint32: + return strconv.FormatUint(uint64(value), 10) + case uint64: + return strconv.FormatUint(value, 10) + case float32: + return strconv.FormatFloat(float64(value), 'g', -1, 64) + case float64: + return strconv.FormatFloat(value, 'g', -1, 64) + case bool: + return strconv.FormatBool(value) + default: + return fmt.Sprintf("%+v", value) + } +} diff --git a/vendor/modules.txt b/vendor/modules.txt index be583c0ba..e1cfc45bb 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -57,6 +57,12 @@ github.com/distribution/distribution/v3/reference ## explicit github.com/emicklei/go-restful github.com/emicklei/go-restful/log +# github.com/emirpasic/gods v1.18.1 +## explicit; go 1.2 +github.com/emirpasic/gods/containers +github.com/emirpasic/gods/trees +github.com/emirpasic/gods/trees/redblacktree +github.com/emirpasic/gods/utils # github.com/evanphx/json-patch v4.12.0+incompatible ## explicit github.com/evanphx/json-patch