pgregory.net/rand@v1.0.3-0.20230808192358-a0b8ce02f4da/rand.go (about) 1 // Copyright 2022 Gregory Petrosyan <gregory.petrosyan@gmail.com> 2 // 3 // This Source Code Form is subject to the terms of the Mozilla Public 4 // License, v. 2.0. If a copy of the MPL was not distributed with this 5 // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 7 // Package rand implements pseudo-random number generators unsuitable for 8 // security-sensitive work. 9 // 10 // Top-level functions that do not have a [Rand] parameter, such as [Float64] and [Int], 11 // use non-deterministic goroutine-local pseudo-random data sources that produce 12 // different sequences of values each time a program is run. These top-level functions 13 // are safe for concurrent use by multiple goroutines, and their performance does 14 // not degrade when the parallelism increases. [Rand] methods and functions with 15 // [Rand] parameter are not safe for concurrent use, but should generally be preferred 16 // because of determinism, higher speed and quality. 17 // 18 // This package is considerably faster and generates higher quality random 19 // than the [math/rand] package. However, this package's outputs might be 20 // predictable regardless of how it's seeded. For random numbers 21 // suitable for security-sensitive work, see the [crypto/rand] package. 22 package rand 23 24 import ( 25 "encoding/binary" 26 "io" 27 "math" 28 "math/bits" 29 ) 30 31 const ( 32 int24Mask = 1<<24 - 1 33 int31Mask = 1<<31 - 1 34 int53Mask = 1<<53 - 1 35 int63Mask = 1<<63 - 1 36 intMask = math.MaxInt 37 38 f24Mul = 0x1.0p-24 39 f53Mul = 0x1.0p-53 40 41 randSizeof = 8*4 + 8 + 1 42 ) 43 44 // Rand is a pseudo-random number generator based on the [SFC64] algorithm by Chris Doty-Humphrey. 45 // 46 // SFC64 has 256 bits of state, average period of ~2^255 and minimum period of at least 2^64. 47 // Generators returned by [New] (with empty or distinct seeds) are guaranteed 48 // to not run into each other for at least 2^64 iterations. 49 // 50 // [SFC64]: http://pracrand.sourceforge.net/RNG_engines.txt 51 type Rand struct { 52 sfc64 53 val uint64 54 pos int 55 } 56 57 // New returns an initialized generator. If seed is empty, generator is initialized to a non-deterministic state. 58 // Otherwise, generator is seeded with the values from seed. New panics if len(seed) > 3. 59 func New(seed ...uint64) *Rand { 60 var r Rand 61 r.new_(seed...) 62 return &r 63 } 64 65 func (r *Rand) new_(seed ...uint64) { 66 switch len(seed) { 67 case 0: 68 r.init0() 69 case 1: 70 r.init1(seed[0]) 71 case 2: 72 r.init3(seed[0], seed[1], 0) 73 case 3: 74 r.init3(seed[0], seed[1], seed[2]) 75 default: 76 panic("invalid New seed sequence length") 77 } 78 } 79 80 // Seed uses the provided seed value to initialize the generator to a deterministic state. 81 func (r *Rand) Seed(seed uint64) { 82 r.init1(seed) 83 r.val = 0 84 r.pos = 0 85 } 86 87 // MarshalBinary returns the binary representation of the current state of the generator. 88 func (r *Rand) MarshalBinary() ([]byte, error) { 89 var data [randSizeof]byte 90 r.marshalBinary(&data) 91 return data[:], nil 92 } 93 94 func (r *Rand) marshalBinary(data *[randSizeof]byte) { 95 binary.LittleEndian.PutUint64(data[0:], r.a) 96 binary.LittleEndian.PutUint64(data[8:], r.b) 97 binary.LittleEndian.PutUint64(data[16:], r.c) 98 binary.LittleEndian.PutUint64(data[24:], r.w) 99 binary.LittleEndian.PutUint64(data[32:], r.val) 100 data[40] = byte(r.pos) 101 } 102 103 // UnmarshalBinary sets the state of the generator to the state represented in data. 104 func (r *Rand) UnmarshalBinary(data []byte) error { 105 if len(data) < randSizeof { 106 return io.ErrUnexpectedEOF 107 } 108 r.a = binary.LittleEndian.Uint64(data[0:]) 109 r.b = binary.LittleEndian.Uint64(data[8:]) 110 r.c = binary.LittleEndian.Uint64(data[16:]) 111 r.w = binary.LittleEndian.Uint64(data[24:]) 112 r.val = binary.LittleEndian.Uint64(data[32:]) 113 r.pos = int(data[40]) 114 return nil 115 } 116 117 // Float32 returns, as a float32, a uniformly distributed pseudo-random number in the half-open interval [0.0, 1.0). 118 func (r *Rand) Float32() float32 { 119 return float32(r.next32()&int24Mask) * f24Mul 120 } 121 122 // Float64 returns, as a float64, a uniformly distributed pseudo-random number in the half-open interval [0.0, 1.0). 123 func (r *Rand) Float64() float64 { 124 return float64(r.next64()&int53Mask) * f53Mul 125 } 126 127 // Int returns a uniformly distributed non-negative pseudo-random int. 128 func (r *Rand) Int() int { 129 return int(r.next64() & intMask) 130 } 131 132 // Int31 returns a uniformly distributed non-negative pseudo-random 31-bit integer as an int32. 133 func (r *Rand) Int31() int32 { 134 return int32(r.next32() & int31Mask) 135 } 136 137 // Int31n returns, as an int32, a uniformly distributed non-negative pseudo-random number 138 // in the half-open interval [0, n). It panics if n <= 0. 139 func (r *Rand) Int31n(n int32) int32 { 140 if n <= 0 { 141 panic("invalid argument to Int31n") 142 } 143 return int32(r.Uint32n(uint32(n))) 144 } 145 146 // Int63 returns a uniformly distributed non-negative pseudo-random 63-bit integer as an int64. 147 func (r *Rand) Int63() int64 { 148 return int64(r.next64() & int63Mask) 149 } 150 151 // Int63n returns, as an int64, a uniformly distributed non-negative pseudo-random number 152 // in the half-open interval [0, n). It panics if n <= 0. 153 func (r *Rand) Int63n(n int64) int64 { 154 if n <= 0 { 155 panic("invalid argument to Int63n") 156 } 157 return int64(r.Uint64n(uint64(n))) 158 } 159 160 // Intn returns, as an int, a uniformly distributed non-negative pseudo-random number 161 // in the half-open interval [0, n). It panics if n <= 0. 162 func (r *Rand) Intn(n int) int { 163 if n <= 0 { 164 panic("invalid argument to Intn") 165 } 166 if math.MaxInt == math.MaxInt32 { 167 return int(r.Uint32n(uint32(n))) 168 } else { 169 return int(r.Uint64n(uint64(n))) 170 } 171 } 172 173 // Perm returns, as a slice of n ints, a pseudo-random permutation of the integers in the half-open interval [0, n). 174 func (r *Rand) Perm(n int) []int { 175 p := make([]int, n) 176 r.perm(p) 177 return p 178 } 179 180 func (r *Rand) perm(p []int) { 181 n := len(p) 182 b := n 183 if b > math.MaxInt32 { 184 b = math.MaxInt32 185 } 186 i := 1 187 for ; i < b; i++ { 188 j := r.Uint32n(uint32(i) + 1) 189 p[i] = p[j] 190 p[j] = i 191 } 192 for ; i < n; i++ { 193 j := r.Uint64n(uint64(i) + 1) 194 p[i] = p[j] 195 p[j] = i 196 } 197 } 198 199 // Read generates len(p) pseudo-random bytes and writes them into p. It always returns len(p) and a nil error. 200 func (r *Rand) Read(p []byte) (n int, err error) { 201 pos := r.pos 202 for ; n < len(p) && n < pos; n++ { 203 p[n] = byte(r.val) 204 r.val >>= 8 205 r.pos-- 206 } 207 for ; n+8 <= len(p); n += 8 { 208 binary.LittleEndian.PutUint64(p[n:n+8], r.next64()) 209 } 210 if n < len(p) { 211 r.val, r.pos = r.next64(), 8 212 for ; n < len(p); n++ { 213 p[n] = byte(r.val) 214 r.val >>= 8 215 r.pos-- 216 } 217 } 218 return 219 } 220 221 // Shuffle pseudo-randomizes the order of elements. n is the number of elements. Shuffle panics if n < 0. 222 // swap swaps the elements with indexes i and j. 223 // 224 // For shuffling elements of a slice, prefer the top-level [ShuffleSlice] function. 225 func (r *Rand) Shuffle(n int, swap func(i, j int)) { 226 if n < 0 { 227 panic("invalid argument to Shuffle") 228 } 229 i := n - 1 230 for ; i > math.MaxInt32-1; i-- { 231 j := int(r.Uint64n(uint64(i) + 1)) 232 swap(i, j) 233 } 234 for ; i > 0; i-- { 235 j := int(r.Uint32n(uint32(i) + 1)) 236 swap(i, j) 237 } 238 } 239 240 // Uint32 returns a uniformly distributed pseudo-random 32-bit value as an uint32. 241 func (r *Rand) Uint32() uint32 { 242 return uint32(r.next32()) 243 } 244 245 // next32 has a bit lower inlining cost because of uint64 return value 246 func (r *Rand) next32() uint64 { 247 // unnatural code to fit into inlining budget of 80 248 if r.pos < 4 { 249 r.val, r.pos = r.next64(), 4 250 return r.val >> 32 251 } else { 252 r.pos = 0 253 return r.val 254 } 255 } 256 257 // Uint32n returns, as an uint32, a uniformly distributed pseudo-random number in [0, n). Uint32n(0) returns 0. 258 func (r *Rand) Uint32n(n uint32) uint32 { 259 // much faster 32-bit version of Uint64n(); result is unbiased with probability 1 - 2^-32. 260 // detecting possible bias would require at least 2^64 samples, which we consider acceptable 261 // since it matches 2^64 guarantees about period length and distance between different seeds. 262 // note that 2^64 is probably a very conservative estimate: scaled down 16-bit version of this 263 // algorithm passes chi-squared test for at least 2^42 (instead of 2^32) values, so 264 // 32-bit version will likely require north of 2^80 values to detect non-uniformity. 265 res, _ := bits.Mul64(uint64(n), r.next64()) 266 return uint32(res) 267 } 268 269 // Uint64 returns a uniformly distributed pseudo-random 64-bit value as an uint64. 270 func (r *Rand) Uint64() uint64 { 271 return r.next64() 272 } 273 274 // Uint64n returns, as an uint64, a uniformly distributed pseudo-random number in [0, n). Uint64n(0) returns 0. 275 func (r *Rand) Uint64n(n uint64) uint64 { 276 // "An optimal algorithm for bounded random integers" by Stephen Canon, https://github.com/apple/swift/pull/39143 277 res, frac := bits.Mul64(n, r.next64()) 278 if n <= math.MaxUint32 { 279 // we don't use frac <= -n check from the original algorithm, since the branch is unpredictable. 280 // instead, we effectively fall back to Uint32n() for 32-bit n 281 return res 282 } 283 hi, _ := bits.Mul64(n, r.next64()) 284 _, carry := bits.Add64(frac, hi, 0) 285 return res + carry 286 }