Merge pull request #2367 from halfrost/cluster-resource-modeling
Cluster resource modeling
This commit is contained in:
commit
aa2419cb1f
1
go.mod
1
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
|
||||
|
|
2
go.sum
2
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=
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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 <benjapurcell@gmail.com>
|
||||
|
||||
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.
|
|
@ -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
|
||||
}
|
|
@ -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{})
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
548
vendor/github.com/emirpasic/gods/trees/redblacktree/redblacktree.go
generated
vendored
Normal file
548
vendor/github.com/emirpasic/gods/trees/redblacktree/redblacktree.go
generated
vendored
Normal file
|
@ -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
|
||||
}
|
48
vendor/github.com/emirpasic/gods/trees/redblacktree/serialization.go
generated
vendored
Normal file
48
vendor/github.com/emirpasic/gods/trees/redblacktree/serialization.go
generated
vendored
Normal file
|
@ -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()
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue