-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathreturns.go
192 lines (171 loc) · 5.12 KB
/
returns.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
package randshiro
import (
"math"
"math/bits"
"reflect"
)
// Returns a uint64 in the interval [0, 2^64)
func (rng *Gen) Uint64() uint64 {
return rng.next()
}
// Returns a uint64 in the interval [0, 2^bitcount)
//
// Makes no range checks on bitcount
func (rng *Gen) Uint64bits(bitcount uint) uint64 {
const bitsInUint64 = 64
return rng.next() >> (bitsInUint64 - bitcount)
}
// Returns a uint64 in the interval [0, bound)
//
// If bound happens to be a power of two, prefer using Uint64bits()
func (rng *Gen) Uint64n(bound uint64) uint64 {
var high, low = bits.Mul64(rng.next(), bound)
if low < bound {
var threshold = -bound % bound
for low < threshold {
high, low = bits.Mul64(rng.next(), bound)
}
}
return high
}
// Returns an int in the interval [0, bound)
//
// Makes no range checks on bound
func (rng *Gen) Intn(bound int) int {
return int(rng.Uint64n(uint64(bound)))
}
// Returns an int in the interval [lowerBound, upperBound)
//
// Makes no range checks on lowerBound/upperBound
func (rng *Gen) IntRange(lowerBound, upperBound int) int {
return rng.Intn(upperBound-lowerBound) + lowerBound
}
// Returns a bool with n in m odds of being true
func (rng *Gen) Odds(n, m uint64) bool {
return rng.Uint64n(m) < n
}
// Returns a bool with 50% odds of being true
func (rng *Gen) Bool() bool {
return rng.Uint64bits(1) == 1
}
// Returns a uniformly distributed float64 in the interval [0.0, 1.0)
//
// Don't cast the float64s produced by this function to float32:
// use Float32() or FastFloat32()
func (rng *Gen) Float64() float64 {
return float64(rng.Uint64bits(float64Bits)) / float64Denom
}
// Returns a uniformly distributed float32 in the interval [0.0, 1.0)
//
// Don't cast the float32s produced by this function to float64:
// use Float64()
func (rng *Gen) Float32() float32 {
return float32(rng.Uint64bits(float32Bits)) / float32Denom
}
// Returns two independent and uniformly distributed float32s in the interval [0.0, 1.0)
//
// Don't cast the float32s produced by this function to float64:
// use Float64()
func (rng *Gen) FastFloat32() (float32, float32) {
var (
random48Bits = rng.Uint64bits(float32Bits * 2)
bottom24Bits = random48Bits & (1<<float32Bits - 1)
upper24Bits = random48Bits >> float32Bits
float2 = float32(bottom24Bits) / float32Denom
float1 = float32(upper24Bits) / float32Denom
)
return float1, float2
}
// Returns two independent and normally distributed float64s
// with mean = 0.0 and stddev = 1.0
//
// Use NormalDist() if you need to adjust mean/stddev
func (rng *Gen) Normal() (float64, float64) {
const bitCount = float64Bits + 1
const shiftValues = 1 << float64Bits
// Manually inlined because it saves some runtime
outer_loop:
// To generate a float64 in the interval (-1.0, 1.0), we roll
// a random number in the interval [0, 2^54), discard rolls of zero,
// and subtract 2^53 (we cast to int64 because we need negatives).
// This gives us an integer in the interval (-2^53, 2^53),
// which then maps to a float64 in the interval (-1.0, 1.0) when we
// do our casting and division magic trick.
inner_loop_1:
var temp = int64(rng.Uint64bits(bitCount))
if temp == 0 {
goto inner_loop_1
}
temp -= shiftValues
var u = float64(temp) / float64Denom
inner_loop_2:
temp = int64(rng.Uint64bits(bitCount))
if temp == 0 {
goto inner_loop_2
}
temp -= shiftValues
var v = float64(temp) / float64Denom
var s = u*u + v*v
if s >= 1 || s == 0 {
goto outer_loop
}
s = math.Sqrt(-2 * math.Log(s) / s)
u *= s
v *= s
return u, v
}
// Returns two independent and normally distributed float64s
// with user-defined mean and stddev
//
// Makes no range checks on mean/stddev
func (rng *Gen) NormalDist(mean, stddev float64) (float64, float64) {
var x, y = rng.Normal()
return x*stddev + mean, y*stddev + mean
}
// Returns an exponentially distributed float64 with
// a rate constant (lambda) of 1
//
// Lambda can be adjusted with: Exponential() / lambda
func (rng *Gen) Exponential() float64 {
// Generated with interval of [0, 2^53)
var temp = rng.Uint64bits(float64Bits)
// Changed to interval of (0, 2^53]
temp++
// Uniformly distributed float64 in the interval (0.0, 1.0]
var float = float64(temp) / float64Denom
// Random variate generation (see docs for link)
return -math.Log(float)
}
// Returns a permutation of ints in the interval [0, n)
//
// Makes no range checks on n
func (rng *Gen) Perm(n int) []int {
var slice = make([]int, n)
for i := range slice {
var j = rng.Intn(i + 1)
slice[i] = slice[j]
slice[j] = i
}
return slice
}
// This method only exists to tell you that the real Shuffle()
// is a function belonging to the randshiro package
func (rng *Gen) Shuffle() {}
// Performs a Fisher-Yates shuffle on the contents of slice
//
// If len(slice) > 1 and rng == nil then
// Shuffle() will instantiate rng with New512pp()
// before continuing as normal. If Shuffle() is being called as a
// one-off it may be preferable to just pass nil to the rng
// parameter.
func Shuffle[T any](rng *Gen, slice []T) {
if len(slice) > 1 {
if rng == nil {
rng = New512pp()
}
var swap = reflect.Swapper(slice)
for i := len(slice) - 1; i > 0; i-- {
swap(i, rng.Intn(i+1))
}
}
}