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