go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/common/data/rand/mathrand/impl.go (about) 1 // Copyright 2016 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package mathrand 16 17 import ( 18 "math/rand" 19 "sync" 20 ) 21 22 // Rand is a random number generator interface. 23 // 24 // A Rand instance is not necessarily safe for concurrent use. In order to 25 // ensure that it is, wrap it in Locking or obtain it from a method that 26 // provides this guarantee (e.g., Get). 27 // 28 // All Rand functions MUST NOT PANIC. In particular, the Locking implementation 29 // relies on embedded methods not panicking in order to release its lock, and 30 // a panic will cause it to hold its lock indefinitely. 31 type Rand interface { 32 // Int63 returns a non-negative pseudo-random 63-bit integer as an int64 33 // from the source in the context or the shared global source. 34 Int63() int64 35 36 // Uint32 returns a pseudo-random 32-bit value as a uint32 from the source in 37 // the context or the shared global source. 38 Uint32() uint32 39 40 // Int31 returns a non-negative pseudo-random 31-bit integer as an int32 from 41 // the source in the context or the shared global source. 42 Int31() int32 43 44 // Int returns a non-negative pseudo-random int from the source in the context 45 // or the shared global source. 46 Int() int 47 48 // Int63n returns, as an int64, a non-negative pseudo-random number in [0,n) 49 // from the source in the context or the shared global source. 50 // 51 // It panics if n <= 0. 52 Int63n(n int64) int64 53 54 // Int31n returns, as an int32, a non-negative pseudo-random number in [0,n) 55 // from the source in the context or the shared global source. 56 // 57 // It panics if n <= 0. 58 Int31n(n int32) int32 59 60 // Intn returns, as an int, a non-negative pseudo-random number in [0,n) from 61 // the source in the context or the shared global source. 62 // 63 // It panics if n <= 0. 64 Intn(n int) int 65 66 // Float64 returns, as a float64, a pseudo-random number in [0.0,1.0) from 67 // the source in the context or the shared global source. 68 Float64() float64 69 70 // Float32 returns, as a float32, a pseudo-random number in [0.0,1.0) from 71 // the source in the context or the shared global source. 72 Float32() float32 73 74 // Perm returns, as a slice of n ints, a pseudo-random permutation of the 75 // integers [0,n) from the source in the context or the shared global source. 76 Perm(n int) []int 77 78 // Read generates len(p) random bytes from the source in the context or 79 // the shared global source and writes them into p. It always returns len(p) 80 // and a nil error. 81 Read(p []byte) (n int, err error) 82 83 // NormFloat64 returns a normally distributed float64 in the range 84 // [-math.MaxFloat64, +math.MaxFloat64] with standard normal distribution 85 // (mean = 0, stddev = 1) from the source in the context or the shared global 86 // source. 87 // 88 // To produce a different normal distribution, callers can adjust the output 89 // using: 90 // 91 // sample = NormFloat64(ctx) * desiredStdDev + desiredMean 92 // 93 NormFloat64() float64 94 95 // ExpFloat64 returns an exponentially distributed float64 in the range 96 // (0, +math.MaxFloat64] with an exponential distribution whose rate parameter 97 // (lambda) is 1 and whose mean is 1/lambda (1) from the source in the context 98 // or the shared global source. 99 // 100 // To produce a distribution with a different rate parameter, callers can adjust 101 // the output using: 102 // 103 // sample = ExpFloat64(ctx) / desiredRateParameter 104 // 105 ExpFloat64() float64 106 107 // WithGoRand invokes the supplied "fn" while holding an exclusive lock 108 // for it. This can be used by callers to pull and use a *rand.Rand instance 109 // out of the Context safely. 110 // 111 // Other "mathrand" functions and objects MUST NOT BE USED inside the 112 // callback, as WithGoRand holds the lock to the current Rand instance, so any 113 // additional function call will deadlock. 114 // 115 // The callback's r must not be retained or used outside of the scope of the 116 // callback. 117 WithGoRand(fn func(r *rand.Rand) error) error 118 } 119 120 // Locking wraps a Rand instance in a layer that locks around all of its 121 // methods. 122 // 123 // A user must hold Locking's Mutex if the want to directly access and use 124 // Locking's R member safely. 125 // 126 // By default, a Rand instance is not safe for concurrent use. A ocking Rand 127 // instance is. 128 type Locking struct { 129 sync.Mutex 130 R Rand 131 } 132 133 func wrapLocking(r Rand) Rand { 134 if _, ok := r.(*Locking); ok { 135 return r 136 } 137 return &Locking{R: r} 138 } 139 140 // Int63 returns a non-negative pseudo-random 63-bit integer as an int64 141 // from the source in the context or the shared global source. 142 func (l *Locking) Int63() (v int64) { 143 l.Lock() 144 v = l.R.Int63() 145 l.Unlock() 146 return 147 } 148 149 // Uint32 returns a pseudo-random 32-bit value as a uint32 from the source in 150 // the context or the shared global source. 151 func (l *Locking) Uint32() (v uint32) { 152 l.Lock() 153 v = l.R.Uint32() 154 l.Unlock() 155 return 156 } 157 158 // Int31 returns a non-negative pseudo-random 31-bit integer as an int32 from 159 // the source in the context or the shared global source. 160 func (l *Locking) Int31() (v int32) { 161 l.Lock() 162 v = l.R.Int31() 163 l.Unlock() 164 return 165 } 166 167 // Int returns a non-negative pseudo-random int from the source in the context 168 // or the shared global source. 169 func (l *Locking) Int() (v int) { 170 l.Lock() 171 v = l.R.Int() 172 l.Unlock() 173 return 174 } 175 176 // Int63n returns, as an int64, a non-negative pseudo-random number in [0,n) 177 // from the source in the context or the shared global source. 178 // 179 // It panics if n <= 0. 180 func (l *Locking) Int63n(n int64) (v int64) { 181 l.Lock() 182 v = l.R.Int63n(n) 183 l.Unlock() 184 return 185 } 186 187 // Int31n returns, as an int32, a non-negative pseudo-random number in [0,n) 188 // from the source in the context or the shared global source. 189 // 190 // It panics if n <= 0. 191 func (l *Locking) Int31n(n int32) (v int32) { 192 l.Lock() 193 v = l.R.Int31n(n) 194 l.Unlock() 195 return 196 } 197 198 // Intn returns, as an int, a non-negative pseudo-random number in [0,n) from 199 // the source in the context or the shared global source. 200 // 201 // It panics if n <= 0. 202 func (l *Locking) Intn(n int) (v int) { 203 l.Lock() 204 v = l.R.Intn(n) 205 l.Unlock() 206 return 207 } 208 209 // Float64 returns, as a float64, a pseudo-random number in [0.0,1.0) from 210 // the source in the context or the shared global source. 211 func (l *Locking) Float64() (v float64) { 212 l.Lock() 213 v = l.R.Float64() 214 l.Unlock() 215 return 216 } 217 218 // Float32 returns, as a float32, a pseudo-random number in [0.0,1.0) from 219 // the source in the context or the shared global source. 220 func (l *Locking) Float32() (v float32) { 221 l.Lock() 222 v = l.R.Float32() 223 l.Unlock() 224 return 225 } 226 227 // Perm returns, as a slice of n ints, a pseudo-random permutation of the 228 // integers [0,n) from the source in the context or the shared global source. 229 func (l *Locking) Perm(n int) (v []int) { 230 l.Lock() 231 v = l.R.Perm(n) 232 l.Unlock() 233 return 234 } 235 236 // Read generates len(p) random bytes from the source in the context or 237 // the shared global source and writes them into p. It always returns len(p) 238 // and a nil error. 239 func (l *Locking) Read(p []byte) (n int, err error) { 240 l.Lock() 241 n, err = l.R.Read(p) 242 l.Unlock() 243 return 244 } 245 246 // NormFloat64 returns a normally distributed float64 in the range 247 // [-math.MaxFloat64, +math.MaxFloat64] with standard normal distribution 248 // (mean = 0, stddev = 1) from the source in the context or the shared global 249 // source. 250 // 251 // To produce a different normal distribution, callers can adjust the output 252 // using: 253 // 254 // sample = NormFloat64(ctx) * desiredStdDev + desiredMean 255 func (l *Locking) NormFloat64() (v float64) { 256 l.Lock() 257 v = l.R.NormFloat64() 258 l.Unlock() 259 return 260 } 261 262 // ExpFloat64 returns an exponentially distributed float64 in the range 263 // (0, +math.MaxFloat64] with an exponential distribution whose rate parameter 264 // (lambda) is 1 and whose mean is 1/lambda (1) from the source in the context 265 // or the shared global source. 266 // 267 // To produce a distribution with a different rate parameter, callers can adjust 268 // the output using: 269 // 270 // sample = ExpFloat64(ctx) / desiredRateParameter 271 func (l *Locking) ExpFloat64() (v float64) { 272 l.Lock() 273 v = l.R.ExpFloat64() 274 l.Unlock() 275 return 276 } 277 278 // WithGoRand invokes the supplied "fn" while holding an exclusive lock 279 // for it. This can be used by callers to pull and use a *rand.Rand instance 280 // out of the Context safely. 281 // 282 // The callback's r must not be retained or used outside of the scope of the 283 // callback. 284 func (l *Locking) WithGoRand(fn func(r *rand.Rand) error) error { 285 l.Lock() 286 defer l.Unlock() 287 return l.R.WithGoRand(fn) 288 } 289 290 // wrapped is a simple wrapper to a *math.Rand instance. 291 type wrapped struct { 292 *rand.Rand 293 } 294 295 // Wrap wraps a Rand instance, allowing it to satisfy the Rand interface. 296 func wrapRand(r *rand.Rand) Rand { return wrapped{r} } 297 298 func (w wrapped) WithGoRand(fn func(r *rand.Rand) error) error { return fn(w.Rand) }