|
125 | 125 |
|
126 | 126 | ### BitInteger
|
127 | 127 |
|
| 128 | +# there are two implemented samplers for unit ranges, which assume that Float64 (i.e. |
| 129 | +# 52 random bits) is the native type for the RNG: |
| 130 | +# 1) "Fast", which is the most efficient when the underlying RNG produces rand(Float64) |
| 131 | +# "fast enough". The tradeoff is faster creation of the sampler, but more |
| 132 | +# consumption of entropy bits |
| 133 | +# 2) "Default" which tries to use as few entropy bits as possible, at the cost of a |
| 134 | +# a bigger upfront price associated with the creation of the sampler |
| 135 | + |
| 136 | +#### Fast |
| 137 | + |
| 138 | +struct SamplerRangeFast{U<:BitUnsigned,T<:Union{BitInteger,Bool}} <: Sampler |
| 139 | + a::T # first element of the range |
| 140 | + bw::UInt # bit width |
| 141 | + m::U # range length - 1 |
| 142 | + mask::U # mask generated values before threshold rejection |
| 143 | +end |
| 144 | + |
| 145 | +function SamplerRangeFast(r::AbstractUnitRange{T}) where T<:Union{Base.BitInteger64,Bool} |
| 146 | + isempty(r) && throw(ArgumentError("range must be non-empty")) |
| 147 | + m = last(r) % UInt64 - first(r) % UInt64 |
| 148 | + bw = (64 - leading_zeros(m)) % UInt # bit-width |
| 149 | + mask = (1 % UInt64 << bw) - (1 % UInt64) |
| 150 | + SamplerRangeFast{UInt64,T}(first(r), bw, m, mask) |
| 151 | +end |
| 152 | + |
| 153 | +function SamplerRangeFast(r::AbstractUnitRange{T}) where T<:Union{Int128,UInt128} |
| 154 | + isempty(r) && throw(ArgumentError("range must be non-empty")) |
| 155 | + m = (last(r)-first(r)) % UInt128 |
| 156 | + bw = (128 - leading_zeros(m)) % UInt # bit-width |
| 157 | + mask = (1 % UInt128 << bw) - (1 % UInt128) |
| 158 | + SamplerRangeFast{UInt128,T}(first(r), bw, m, mask) |
| 159 | +end |
| 160 | + |
| 161 | +function rand_lteq(r::AbstractRNG, randfun, u::U, mask::U) where U<:Integer |
| 162 | + while true |
| 163 | + x = randfun(r) & mask |
| 164 | + x <= u && return x |
| 165 | + end |
| 166 | +end |
| 167 | + |
| 168 | +function rand(rng::AbstractRNG, sp::SamplerRangeFast{UInt64,T}) where T |
| 169 | + a, bw, m, mask = sp.a, sp.bw, sp.m, sp.mask |
| 170 | + x = bw <= 52 ? rand_lteq(rng, rand_ui52_raw, m, mask) : |
| 171 | + rand_lteq(rng, rng->rand(rng, UInt64), m, mask) |
| 172 | + (x + a % UInt64) % T |
| 173 | +end |
| 174 | + |
| 175 | +function rand(rng::AbstractRNG, sp::SamplerRangeFast{UInt128,T}) where T |
| 176 | + a, bw, m, mask = sp.a, sp.bw, sp.m, sp.mask |
| 177 | + x = bw <= 52 ? rand_lteq(rng, rand_ui52_raw, m % UInt64, mask % UInt64) % UInt128 : |
| 178 | + bw <= 104 ? rand_lteq(rng, rand_ui104_raw, m, mask) : |
| 179 | + rand_lteq(rng, rng->rand(rng, UInt128), m, mask) |
| 180 | + x % T + a |
| 181 | +end |
| 182 | + |
| 183 | +#### Default |
| 184 | + |
128 | 185 | # remainder function according to Knuth, where rem_knuth(a, 0) = a
|
129 | 186 | rem_knuth(a::UInt, b::UInt) = a % (b + (b == 0)) + a * (b == 0)
|
130 | 187 | rem_knuth(a::T, b::T) where {T<:Unsigned} = b != 0 ? a % b : a
|
|
0 commit comments