-
Notifications
You must be signed in to change notification settings - Fork 1
/
rnd.go
72 lines (62 loc) · 1.7 KB
/
rnd.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
package gg
import (
"crypto/rand"
"io"
"math/big"
)
/*
Generates a random integer of the given type, using the given entropy source
(typically `"crypto/rand".Reader`).
*/
func RandomInt[A Int](src io.Reader) (out A) {
Try1(src.Read(AsBytes(&out)))
return out
}
/*
Generates a random integer in the range `[min,max)`, using the given entropy
source (typically `"crypto/rand".Reader`). All numbers must be below
`math.MaxInt64`.
*/
func RandomIntBetween[A Int](src io.Reader, min, max A) A {
if !(max > min) {
panic(Errf(`invalid range [%v,%v)`, min, max))
}
// The following is suboptimal. See implementation notes below.
minInt := NumConv[int64](min)
maxInt := NumConv[int64](max)
maxBig := new(big.Int).SetInt64(maxInt - minInt)
tarBig := Try1(rand.Int(src, maxBig))
return min + NumConv[A](tarBig.Int64())
}
/*
The following implementation doesn't fully pass our test. It performs marginally
better than the wrapper around the "crypto/rand" version. TODO fix. Also TODO
generalize for all int types.
func RandomUint64Between(src io.Reader, min, max uint64) (out uint64) {
if !(max > min) {
panic(Errf(`invalid range [%v,%v)`, min, max))
}
ceil := max - min
bits := bits.Len64(ceil)
buf := AsBytes(&out)[:(bits+7)/8]
for {
Try1(src.Read(buf))
buf[0] >>= (8 - (bits % 8))
out = 0
for _, byte := range buf {
out = (out << 8) | uint64(byte)
}
if out < ceil {
return out + min
}
}
}
*/
/*
Picks a random element from the given slice, using the given entropy source
(typically `"crypto/rand".Reader`). Panics if the slice is empty or the reader
is nil.
*/
func RandomElem[A any](reader io.Reader, slice []A) A {
return slice[RandomIntBetween(reader, 0, len(slice))]
}