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  }