mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 13:28:01 -05:00
Compare commits
5 Commits
c6c9414d8b
...
constraint
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d64197b33d | ||
|
|
0cc87d5d2c | ||
|
|
df4dde5104 | ||
|
|
420e2c6208 | ||
|
|
ec955a14ee |
8
container/constraints/BUILD.bazel
Normal file
8
container/constraints/BUILD.bazel
Normal file
@@ -0,0 +1,8 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["constraints.go"],
|
||||
importpath = "github.com/prysmaticlabs/prysm/container/constraints",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
46
container/constraints/constraints.go
Normal file
46
container/constraints/constraints.go
Normal file
@@ -0,0 +1,46 @@
|
||||
// Package constraints defines a set of useful constraints to be used
|
||||
// with type parameters.
|
||||
package constraints
|
||||
|
||||
// Signed is a constraint that permits any signed integer type.
|
||||
// If future releases of Go add new predeclared signed integer types,
|
||||
// this constraint will be modified to include them.
|
||||
type Signed interface {
|
||||
~int | ~int8 | ~int16 | ~int32 | ~int64
|
||||
}
|
||||
|
||||
// Unsigned is a constraint that permits any unsigned integer type.
|
||||
// If future releases of Go add new predeclared unsigned integer types,
|
||||
// this constraint will be modified to include them.
|
||||
type Unsigned interface {
|
||||
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
|
||||
}
|
||||
|
||||
// Integer is a constraint that permits any integer type.
|
||||
// If future releases of Go add new predeclared integer types,
|
||||
// this constraint will be modified to include them.
|
||||
type Integer interface {
|
||||
Signed | Unsigned
|
||||
}
|
||||
|
||||
// Float is a constraint that permits any floating-point type.
|
||||
// If future releases of Go add new predeclared floating-point types,
|
||||
// this constraint will be modified to include them.
|
||||
type Float interface {
|
||||
~float32 | ~float64
|
||||
}
|
||||
|
||||
// Complex is a constraint that permits any complex numeric type.
|
||||
// If future releases of Go add new predeclared complex numeric types,
|
||||
// this constraint will be modified to include them.
|
||||
type Complex interface {
|
||||
~complex64 | ~complex128
|
||||
}
|
||||
|
||||
// Ordered is a constraint that permits any ordered type: any type
|
||||
// that supports the operators < <= >= >.
|
||||
// If future releases of Go add new ordered types,
|
||||
// this constraint will be modified to include them.
|
||||
type Ordered interface {
|
||||
Integer | Float | ~string
|
||||
}
|
||||
@@ -8,7 +8,10 @@ go_library(
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/container/slice",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = ["//consensus-types/primitives:go_default_library"],
|
||||
deps = [
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//container/constraints:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
@@ -18,5 +21,6 @@ go_test(
|
||||
deps = [
|
||||
":go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//crypto/rand:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -4,8 +4,150 @@ import (
|
||||
"strings"
|
||||
|
||||
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/container/constraints"
|
||||
)
|
||||
|
||||
// Subset returns true if the first slice is
|
||||
// completely contained in the second slice with time
|
||||
// complexity of approximately o(n).
|
||||
func Subset[T constraints.Ordered](a, b []T) bool {
|
||||
if len(a) > len(b) {
|
||||
return false
|
||||
}
|
||||
|
||||
set := make(map[T]uint64, len(b))
|
||||
for _, v := range b {
|
||||
set[v]++
|
||||
}
|
||||
|
||||
for _, v := range a {
|
||||
if count, found := set[v]; !found {
|
||||
return false
|
||||
} else if count < 1 {
|
||||
return false
|
||||
} else {
|
||||
set[v] = count - 1
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Intersection of any number of slices with time
|
||||
// complexity of approximately O(n) leveraging a map to
|
||||
// check for element existence off by a constant factor
|
||||
// of underlying map efficiency.
|
||||
func Intersection[T constraints.Ordered](s ...[]T) []T {
|
||||
if len(s) == 0 {
|
||||
return []T{}
|
||||
}
|
||||
if len(s) == 1 {
|
||||
return s[0]
|
||||
}
|
||||
intersect := make([]T, 0)
|
||||
m := make(map[T]int)
|
||||
for _, k := range s[0] {
|
||||
m[k] = 1
|
||||
}
|
||||
for i, num := 1, len(s); i < num; i++ {
|
||||
for _, k := range s[i] {
|
||||
// Increment and check only if item is present in both, and no increment has happened yet.
|
||||
if _, found := m[k]; found && i == m[k] {
|
||||
m[k]++
|
||||
if m[k] == num {
|
||||
intersect = append(intersect, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return intersect
|
||||
}
|
||||
|
||||
// Union of any number of uint64 slices with time
|
||||
// complexity of approximately O(n) leveraging a map to
|
||||
// check for element existence off by a constant factor
|
||||
// of underlying map efficiency.
|
||||
func Union[T constraints.Ordered](s ...[]T) []T {
|
||||
if len(s) == 0 {
|
||||
return []T{}
|
||||
}
|
||||
if len(s) == 1 {
|
||||
return s[0]
|
||||
}
|
||||
set := s[0]
|
||||
m := make(map[T]bool)
|
||||
for i := 1; i < len(s); i++ {
|
||||
a := s[i-1]
|
||||
b := s[i]
|
||||
for j := 0; j < len(a); j++ {
|
||||
m[a[j]] = true
|
||||
}
|
||||
for j := 0; j < len(b); j++ {
|
||||
if _, found := m[b[j]]; !found {
|
||||
set = append(set, b[j])
|
||||
}
|
||||
}
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// Uniq returns a slice with only unique
|
||||
// values from the provided list of indices.
|
||||
func Uniq[T constraints.Ordered](a []T) []T {
|
||||
// Remove duplicates indices.
|
||||
intMap := map[T]bool{}
|
||||
cleanedIndices := make([]T, 0, len(a))
|
||||
for _, idx := range a {
|
||||
if intMap[idx] {
|
||||
continue
|
||||
}
|
||||
intMap[idx] = true
|
||||
cleanedIndices = append(cleanedIndices, idx)
|
||||
}
|
||||
return cleanedIndices
|
||||
}
|
||||
|
||||
// IsSorted verifies if a slice is sorted in ascending order.
|
||||
func IsSorted[T constraints.Ordered](a []T) bool {
|
||||
if len(a) == 0 || len(a) == 1 {
|
||||
return true
|
||||
}
|
||||
for i := 1; i < len(a); i++ {
|
||||
if a[i-1] > a[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// NotIn returns the items in slice b that are
|
||||
// not in slice a with time complexity of approximately
|
||||
// O(n) leveraging a map to check for element existence
|
||||
// off by a constant factor of underlying map efficiency.
|
||||
func NotIn[T constraints.Ordered](a, b []T) []T {
|
||||
set := make([]T, 0)
|
||||
m := make(map[T]bool)
|
||||
|
||||
for i := 0; i < len(a); i++ {
|
||||
m[a[i]] = true
|
||||
}
|
||||
for i := 0; i < len(b); i++ {
|
||||
if _, found := m[b[i]]; !found {
|
||||
set = append(set, b[i])
|
||||
}
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// IsIn returns true if a is in b and False otherwise.
|
||||
func IsIn[T constraints.Ordered](a T, b []T) bool {
|
||||
for _, v := range b {
|
||||
if a == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SubsetUint64 returns true if the first array is
|
||||
// completely contained in the second array with time
|
||||
// complexity of approximately o(n).
|
||||
|
||||
@@ -7,9 +7,10 @@ import (
|
||||
|
||||
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/container/slice"
|
||||
"github.com/prysmaticlabs/prysm/crypto/rand"
|
||||
)
|
||||
|
||||
func TestSubsetUint64(t *testing.T) {
|
||||
func TestSubset(t *testing.T) {
|
||||
testCases := []struct {
|
||||
setA []uint64
|
||||
setB []uint64
|
||||
@@ -30,7 +31,28 @@ func TestSubsetUint64(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntersectionUint64(t *testing.T) {
|
||||
func TestSubset_Generic(t *testing.T) {
|
||||
testCases := []struct {
|
||||
setA []uint64
|
||||
setB []uint64
|
||||
out bool
|
||||
}{
|
||||
{[]uint64{1}, []uint64{1, 2, 3, 4}, true},
|
||||
{[]uint64{1, 2, 3, 4}, []uint64{1, 2, 3, 4}, true},
|
||||
{[]uint64{1, 1}, []uint64{1, 2, 3, 4}, false},
|
||||
{[]uint64{}, []uint64{1}, true},
|
||||
{[]uint64{1}, []uint64{}, false},
|
||||
{[]uint64{1, 2, 3, 4, 5}, []uint64{1, 2, 3, 4}, false},
|
||||
}
|
||||
for _, tt := range testCases {
|
||||
result := slice.Subset[uint64](tt.setA, tt.setB)
|
||||
if result != tt.out {
|
||||
t.Errorf("%v, got %v, want %v", tt.setA, result, tt.out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntersection(t *testing.T) {
|
||||
testCases := []struct {
|
||||
setA []uint64
|
||||
setB []uint64
|
||||
@@ -76,7 +98,53 @@ func TestIntersectionUint64(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsSortedUint64(t *testing.T) {
|
||||
func TestIntersection_Generic(t *testing.T) {
|
||||
testCases := []struct {
|
||||
setA []uint64
|
||||
setB []uint64
|
||||
setC []uint64
|
||||
out []uint64
|
||||
}{
|
||||
{[]uint64{2, 3, 5}, []uint64{3}, []uint64{3}, []uint64{3}},
|
||||
{[]uint64{2, 3, 5}, []uint64{3, 5}, []uint64{5}, []uint64{5}},
|
||||
{[]uint64{2, 3, 5}, []uint64{3, 5}, []uint64{3, 5}, []uint64{3, 5}},
|
||||
{[]uint64{2, 3, 5}, []uint64{5, 3, 2}, []uint64{3, 2, 5}, []uint64{2, 3, 5}},
|
||||
{[]uint64{3, 2, 5}, []uint64{5, 3, 2}, []uint64{3, 2, 5}, []uint64{2, 3, 5}},
|
||||
{[]uint64{3, 3, 5}, []uint64{5, 3, 2}, []uint64{3, 2, 5}, []uint64{3, 5}},
|
||||
{[]uint64{2, 3, 5}, []uint64{2, 3, 5}, []uint64{2, 3, 5}, []uint64{2, 3, 5}},
|
||||
{[]uint64{2, 3, 5}, []uint64{}, []uint64{}, []uint64{}},
|
||||
{[]uint64{2, 3, 5}, []uint64{2, 3, 5}, []uint64{}, []uint64{}},
|
||||
{[]uint64{2, 3}, []uint64{2, 3, 5}, []uint64{5}, []uint64{}},
|
||||
{[]uint64{2, 2, 2}, []uint64{2, 2, 2}, []uint64{}, []uint64{}},
|
||||
{[]uint64{}, []uint64{2, 3, 5}, []uint64{}, []uint64{}},
|
||||
{[]uint64{}, []uint64{}, []uint64{}, []uint64{}},
|
||||
{[]uint64{1}, []uint64{1}, []uint64{}, []uint64{}},
|
||||
{[]uint64{1, 1, 1}, []uint64{1, 1}, []uint64{1, 2, 3}, []uint64{1}},
|
||||
}
|
||||
for _, tt := range testCases {
|
||||
setA := append([]uint64{}, tt.setA...)
|
||||
setB := append([]uint64{}, tt.setB...)
|
||||
setC := append([]uint64{}, tt.setC...)
|
||||
result := slice.Intersection[uint64](setA, setB, setC)
|
||||
sort.Slice(result, func(i, j int) bool {
|
||||
return result[i] < result[j]
|
||||
})
|
||||
if !reflect.DeepEqual(result, tt.out) {
|
||||
t.Errorf("got %d, want %d", result, tt.out)
|
||||
}
|
||||
if !reflect.DeepEqual(setA, tt.setA) {
|
||||
t.Errorf("slice modified, got %v, want %v", setA, tt.setA)
|
||||
}
|
||||
if !reflect.DeepEqual(setB, tt.setB) {
|
||||
t.Errorf("slice modified, got %v, want %v", setB, tt.setB)
|
||||
}
|
||||
if !reflect.DeepEqual(setC, tt.setC) {
|
||||
t.Errorf("slice modified, got %v, want %v", setC, tt.setC)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsSorted(t *testing.T) {
|
||||
testCases := []struct {
|
||||
setA []uint64
|
||||
out bool
|
||||
@@ -94,6 +162,24 @@ func TestIsSortedUint64(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsSorted_Generic(t *testing.T) {
|
||||
testCases := []struct {
|
||||
setA []uint64
|
||||
out bool
|
||||
}{
|
||||
{[]uint64{1, 2, 3}, true},
|
||||
{[]uint64{3, 1, 3}, false},
|
||||
{[]uint64{1}, true},
|
||||
{[]uint64{}, true},
|
||||
}
|
||||
for _, tt := range testCases {
|
||||
result := slice.IsSorted[uint64](tt.setA)
|
||||
if result != tt.out {
|
||||
t.Errorf("got %v, want %v", result, tt.out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntersectionInt64(t *testing.T) {
|
||||
testCases := []struct {
|
||||
setA []int64
|
||||
@@ -205,6 +291,39 @@ func TestUnionInt64(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnion_Generic(t *testing.T) {
|
||||
testCases := []struct {
|
||||
setA []uint64
|
||||
setB []uint64
|
||||
out []uint64
|
||||
}{
|
||||
{[]uint64{2, 3, 5}, []uint64{4, 6}, []uint64{2, 3, 5, 4, 6}},
|
||||
{[]uint64{2, 3, 5}, []uint64{3, 5}, []uint64{2, 3, 5}},
|
||||
{[]uint64{2, 3, 5}, []uint64{2, 3, 5}, []uint64{2, 3, 5}},
|
||||
{[]uint64{2, 3, 5}, []uint64{}, []uint64{2, 3, 5}},
|
||||
{[]uint64{}, []uint64{2, 3, 5}, []uint64{2, 3, 5}},
|
||||
{[]uint64{}, []uint64{}, []uint64{}},
|
||||
{[]uint64{1}, []uint64{1}, []uint64{1}},
|
||||
}
|
||||
for _, tt := range testCases {
|
||||
result := slice.Union[uint64](tt.setA, tt.setB)
|
||||
if !reflect.DeepEqual(result, tt.out) {
|
||||
t.Errorf("got %d, want %d", result, tt.out)
|
||||
}
|
||||
|
||||
}
|
||||
items := [][]uint64{
|
||||
{3, 4, 5},
|
||||
{6, 7, 8},
|
||||
{9, 10, 11},
|
||||
}
|
||||
variadicResult := slice.Union[uint64](items...)
|
||||
want := []uint64{3, 4, 5, 6, 7, 8, 9, 10, 11}
|
||||
if !reflect.DeepEqual(want, variadicResult) {
|
||||
t.Errorf("Received %v, wanted %v", variadicResult, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCleanUint64(t *testing.T) {
|
||||
testCases := []struct {
|
||||
in []uint64
|
||||
@@ -225,6 +344,26 @@ func TestCleanUint64(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUniq_Generic(t *testing.T) {
|
||||
testCases := []struct {
|
||||
in []uint64
|
||||
out []uint64
|
||||
}{
|
||||
{[]uint64{2, 4, 4, 6, 6}, []uint64{2, 4, 6}},
|
||||
{[]uint64{3, 5, 5}, []uint64{3, 5}},
|
||||
{[]uint64{2, 2, 2}, []uint64{2}},
|
||||
{[]uint64{1, 4, 5, 9, 9}, []uint64{1, 4, 5, 9}},
|
||||
{[]uint64{}, []uint64{}},
|
||||
{[]uint64{1}, []uint64{1}},
|
||||
}
|
||||
for _, tt := range testCases {
|
||||
result := slice.Uniq[uint64](tt.in)
|
||||
if !reflect.DeepEqual(result, tt.out) {
|
||||
t.Errorf("got %d, want %d", result, tt.out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotUint64(t *testing.T) {
|
||||
testCases := []struct {
|
||||
setA []uint64
|
||||
@@ -247,6 +386,28 @@ func TestNotUint64(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotIn_Generic(t *testing.T) {
|
||||
testCases := []struct {
|
||||
setA []uint64
|
||||
setB []uint64
|
||||
out []uint64
|
||||
}{
|
||||
{[]uint64{4, 6}, []uint64{2, 3, 5, 4, 6}, []uint64{2, 3, 5}},
|
||||
{[]uint64{3, 5}, []uint64{2, 3, 5}, []uint64{2}},
|
||||
{[]uint64{2, 3, 5}, []uint64{2, 3, 5}, []uint64{}},
|
||||
{[]uint64{2}, []uint64{2, 3, 5}, []uint64{3, 5}},
|
||||
{[]uint64{}, []uint64{2, 3, 5}, []uint64{2, 3, 5}},
|
||||
{[]uint64{}, []uint64{}, []uint64{}},
|
||||
{[]uint64{1}, []uint64{1}, []uint64{}},
|
||||
}
|
||||
for _, tt := range testCases {
|
||||
result := slice.NotIn[uint64](tt.setA, tt.setB)
|
||||
if !reflect.DeepEqual(result, tt.out) {
|
||||
t.Errorf("got %d, want %d", result, tt.out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotInt64(t *testing.T) {
|
||||
testCases := []struct {
|
||||
setA []int64
|
||||
@@ -309,6 +470,26 @@ func TestIsInInt64(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsIn_Generic(t *testing.T) {
|
||||
testCases := []struct {
|
||||
a uint64
|
||||
b []uint64
|
||||
result bool
|
||||
}{
|
||||
{0, []uint64{}, false},
|
||||
{0, []uint64{0}, true},
|
||||
{4, []uint64{2, 3, 5, 4, 6}, true},
|
||||
{100, []uint64{2, 3, 5, 4, 6}, false},
|
||||
}
|
||||
for _, tt := range testCases {
|
||||
result := slice.IsIn[uint64](tt.a, tt.b)
|
||||
if result != tt.result {
|
||||
t.Errorf("IsIn(%d, %v)=%v, wanted: %v",
|
||||
tt.a, tt.b, result, tt.result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnionByteSlices(t *testing.T) {
|
||||
testCases := []struct {
|
||||
setA [][]byte
|
||||
@@ -587,3 +768,81 @@ func TestIsInSlots(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIntersection_Specific(b *testing.B) {
|
||||
b.StopTimer()
|
||||
sets := setupSlices()
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
slice.IntersectionUint64(sets...)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIntersection_Generic(b *testing.B) {
|
||||
b.StopTimer()
|
||||
sets := setupSlices()
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
slice.Intersection[uint64](sets...)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnion_Specific(b *testing.B) {
|
||||
b.StopTimer()
|
||||
sets := setupSlices()
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
slice.UnionUint64(sets...)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnion_Generic(b *testing.B) {
|
||||
b.StopTimer()
|
||||
sets := setupSlices()
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
slice.Union[uint64](sets...)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUniq_Specific(b *testing.B) {
|
||||
b.StopTimer()
|
||||
input := make([]uint64, 0, 1000)
|
||||
gen := rand.NewGenerator()
|
||||
for i := 0; i < 1000; i++ {
|
||||
n := gen.Uint64()
|
||||
input = append(input, n, n)
|
||||
}
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
slice.SetUint64(input)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUniq_Generic(b *testing.B) {
|
||||
b.StopTimer()
|
||||
input := make([]uint64, 0, 1000)
|
||||
gen := rand.NewGenerator()
|
||||
for i := 0; i < 1000; i++ {
|
||||
n := gen.Uint64()
|
||||
input = append(input, n, n)
|
||||
}
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
slice.Uniq[uint64](input)
|
||||
}
|
||||
}
|
||||
|
||||
func setupSlices() [][]uint64 {
|
||||
sets := make([][]uint64, 1000)
|
||||
gen := rand.NewGenerator()
|
||||
for i := 0; i < len(sets); i++ {
|
||||
setSize := gen.Intn(100)
|
||||
set := make([]uint64, setSize)
|
||||
for j := 0; j < setSize; j++ {
|
||||
set[j] = uint64(j)
|
||||
}
|
||||
sets[i] = set
|
||||
}
|
||||
return sets
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user