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) }