mirror of
https://github.com/luxfi/node.git
synced 2026-07-27 03:39:39 +00:00
87 lines
2.2 KiB
Go
87 lines
2.2 KiB
Go
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package sampler
|
|
|
|
import (
|
|
"math"
|
|
"sync"
|
|
"time"
|
|
|
|
"gonum.org/v1/gonum/mathext/prng"
|
|
)
|
|
|
|
var globalRNG = newRNG()
|
|
|
|
func newRNG() *rng {
|
|
// We don't use a cryptographically secure source of randomness here, as
|
|
// there's no need to ensure a truly random sampling.
|
|
source := prng.NewMT19937()
|
|
source.Seed(uint64(time.Now().UnixNano()))
|
|
return &rng{rng: source}
|
|
}
|
|
|
|
type rng struct {
|
|
lock sync.Mutex
|
|
rng Source
|
|
}
|
|
|
|
type Source interface {
|
|
// Uint64 returns a random number in [0, MaxUint64] and advances the
|
|
// generator's state.
|
|
Uint64() uint64
|
|
}
|
|
|
|
// Uint64Inclusive returns a pseudo-random number in [0,n].
|
|
//
|
|
// Invariant: The result of this function is stored in chain state, so any
|
|
// modifications are considered breaking.
|
|
func (r *rng) Uint64Inclusive(n uint64) uint64 {
|
|
switch {
|
|
// n+1 is power of two, so we can just mask
|
|
//
|
|
// Note: This does work for MaxUint64 as overflow is explicitly part of the
|
|
// compiler specification: https://go.dev/ref/spec#Integer_overflow
|
|
case n&(n+1) == 0:
|
|
return r.uint64() & n
|
|
|
|
// n is greater than MaxUint64/2 so we need to just iterate until we get a
|
|
// number in the requested range.
|
|
case n > math.MaxInt64:
|
|
v := r.uint64()
|
|
for v > n {
|
|
v = r.uint64()
|
|
}
|
|
return v
|
|
|
|
// n is less than MaxUint64/2 so we generate a number in the range
|
|
// [0, k*(n+1)) where k is the largest integer such that k*(n+1) is less
|
|
// than or equal to MaxUint64/2. We can't easily find k such that k*(n+1) is
|
|
// less than or equal to MaxUint64 because the calculation would overflow.
|
|
//
|
|
// ref: https://github.com/golang/go/blob/ce10e9d84574112b224eae88dc4e0f43710808de/src/math/rand/rand.go#L127-L132
|
|
default:
|
|
maximum := (1 << 63) - 1 - (1<<63)%(n+1)
|
|
v := r.uint63()
|
|
for v > maximum {
|
|
v = r.uint63()
|
|
}
|
|
return v % (n + 1)
|
|
}
|
|
}
|
|
|
|
// uint63 returns a random number in [0, MaxInt64]
|
|
func (r *rng) uint63() uint64 {
|
|
return r.uint64() & math.MaxInt64
|
|
}
|
|
|
|
// uint64 returns a random number in [0, MaxUint64]
|
|
func (r *rng) uint64() uint64 {
|
|
// Note: We must grab a write lock here because rng.Uint64 internally
|
|
// modifies state.
|
|
r.lock.Lock()
|
|
n := r.rng.Uint64()
|
|
r.lock.Unlock()
|
|
return n
|
|
}
|