@@ -18,37 +18,51 @@ package shufflesharding
18
18
19
19
import (
20
20
"errors"
21
+ "fmt"
21
22
"math"
23
+ "strings"
22
24
)
23
25
24
26
const maxHashBits = 60
25
27
26
- // ValidateParameters can validate parameters for shuffle sharding
27
- // in a fast but approximate way, including deckSize and handSize
28
- // Algorithm: maxHashBits >= bits(deckSize^handSize)
29
- func ValidateParameters (deckSize , handSize int32 ) bool {
30
- if handSize <= 0 || deckSize <= 0 || handSize > deckSize {
31
- return false
28
+ // ValidateParameters finds errors in the parameters for shuffle
29
+ // sharding. Returns a slice for which `len()` is 0 if and only if
30
+ // there are no errors. The entropy requirement is evaluated in a
31
+ // fast but approximate way: bits(deckSize^handSize).
32
+ func ValidateParameters (deckSize , handSize int ) (errs []string ) {
33
+ if handSize <= 0 {
34
+ errs = append (errs , "handSize is not positive" )
32
35
}
33
-
34
- return math .Log2 (float64 (deckSize ))* float64 (handSize ) <= maxHashBits
36
+ if deckSize <= 0 {
37
+ errs = append (errs , "deckSize is not positive" )
38
+ }
39
+ if len (errs ) > 0 {
40
+ return
41
+ }
42
+ if handSize > deckSize {
43
+ return []string {"handSize is greater than deckSize" }
44
+ }
45
+ if math .Log2 (float64 (deckSize ))* float64 (handSize ) > maxHashBits {
46
+ return []string {fmt .Sprintf ("more than %d bits of entropy required" , maxHashBits )}
47
+ }
48
+ return
35
49
}
36
50
37
51
// ShuffleAndDeal can shuffle a hash value to handSize-quantity and non-redundant
38
52
// indices of decks, with the pick function, we can get the optimal deck index
39
53
// Eg. From deckSize=128, handSize=8, we can get an index array [12 14 73 18 119 51 117 26],
40
54
// then pick function will choose the optimal index from these
41
55
// Algorithm: https://github.com/kubernetes/enhancements/blob/master/keps/sig-api-machinery/20190228-priority-and-fairness.md#queue-assignment-proof-of-concept
42
- func ShuffleAndDeal (hashValue uint64 , deckSize , handSize int32 , pick func (int32 )) {
43
- remainders := make ([]int32 , handSize )
56
+ func ShuffleAndDeal (hashValue uint64 , deckSize , handSize int , pick func (int )) {
57
+ remainders := make ([]int , handSize )
44
58
45
- for i := int32 ( 0 ) ; i < handSize ; i ++ {
59
+ for i := 0 ; i < handSize ; i ++ {
46
60
hashValueNext := hashValue / uint64 (deckSize - i )
47
- remainders [i ] = int32 (hashValue - uint64 (deckSize - i )* hashValueNext )
61
+ remainders [i ] = int (hashValue - uint64 (deckSize - i )* hashValueNext )
48
62
hashValue = hashValueNext
49
63
}
50
64
51
- for i := int32 ( 0 ) ; i < handSize ; i ++ {
65
+ for i := 0 ; i < handSize ; i ++ {
52
66
candidate := remainders [i ]
53
67
for j := i ; j > 0 ; j -- {
54
68
if candidate >= remainders [j - 1 ] {
@@ -60,9 +74,9 @@ func ShuffleAndDeal(hashValue uint64, deckSize, handSize int32, pick func(int32)
60
74
}
61
75
62
76
// ShuffleAndDealWithValidation will do validation before ShuffleAndDeal
63
- func ShuffleAndDealWithValidation (hashValue uint64 , deckSize , handSize int32 , pick func (int32 )) error {
64
- if ! ValidateParameters (deckSize , handSize ) {
65
- return errors .New ("bad parameters" )
77
+ func ShuffleAndDealWithValidation (hashValue uint64 , deckSize , handSize int , pick func (int )) error {
78
+ if errs := ValidateParameters (deckSize , handSize ); len ( errs ) > 0 {
79
+ return errors .New (strings . Join ( errs , ";" ) )
66
80
}
67
81
68
82
ShuffleAndDeal (hashValue , deckSize , handSize , pick )
@@ -71,14 +85,14 @@ func ShuffleAndDealWithValidation(hashValue uint64, deckSize, handSize int32, pi
71
85
72
86
// ShuffleAndDealToSlice will use specific pick function to return slices of indices
73
87
// after ShuffleAndDeal
74
- func ShuffleAndDealToSlice (hashValue uint64 , deckSize , handSize int32 ) []int32 {
88
+ func ShuffleAndDealToSlice (hashValue uint64 , deckSize , handSize int ) []int {
75
89
var (
76
- candidates = make ([]int32 , handSize )
90
+ candidates = make ([]int , handSize )
77
91
idx = 0
78
92
)
79
93
80
- pickToSlices := func (can int32 ) {
81
- candidates [idx ] = can
94
+ pickToSlices := func (can int ) {
95
+ candidates [idx ] = int ( can )
82
96
idx ++
83
97
}
84
98
@@ -87,11 +101,33 @@ func ShuffleAndDealToSlice(hashValue uint64, deckSize, handSize int32) []int32 {
87
101
return candidates
88
102
}
89
103
104
+ // ShuffleAndDealIntoHand shuffles a deck of the given size by the
105
+ // given hash value and deals cards into the given slice. The virtue
106
+ // of this function compared to ShuffleAndDealToSlice is that the
107
+ // caller provides the storage for the hand.
108
+ func ShuffleAndDealIntoHand (hashValue uint64 , deckSize int , hand []int ) {
109
+ handSize := len (hand )
110
+ var idx int
111
+ ShuffleAndDeal (hashValue , deckSize , handSize , func (card int ) {
112
+ hand [idx ] = int (card )
113
+ idx ++
114
+ })
115
+ }
116
+
90
117
// ShuffleAndDealToSliceWithValidation will do validation before ShuffleAndDealToSlice
91
- func ShuffleAndDealToSliceWithValidation (hashValue uint64 , deckSize , handSize int32 ) ([]int32 , error ) {
92
- if ! ValidateParameters (deckSize , handSize ) {
93
- return nil , errors .New ("bad parameters" )
118
+ func ShuffleAndDealToSliceWithValidation (hashValue uint64 , deckSize , handSize int ) ([]int , error ) {
119
+ if errs := ValidateParameters (deckSize , handSize ); len ( errs ) > 0 {
120
+ return nil , errors .New (strings . Join ( errs , ";" ) )
94
121
}
95
122
96
123
return ShuffleAndDealToSlice (hashValue , deckSize , handSize ), nil
97
124
}
125
+
126
+ // ShuffleAndDealIntoHandWithValidation does validation and then ShuffleAndDealIntoHand
127
+ func ShuffleAndDealIntoHandWithValidation (hashValue uint64 , deckSize int , hand []int ) error {
128
+ if errs := ValidateParameters (deckSize , len (hand )); len (errs ) > 0 {
129
+ return errors .New (strings .Join (errs , ";" ))
130
+ }
131
+ ShuffleAndDealIntoHand (hashValue , deckSize , hand )
132
+ return nil
133
+ }
0 commit comments