Skip to content

Commit ea2bd4b

Browse files
committed
restrict RandIntGen to generate random numbers in the interval [1,n]
This is based on @ivarne idea (cf. JuliaLang#8255 (comment)), and continues commit 48f27bc in "avoiding duplicating the getindex logic". The API to get a RandIntGen object is now to call randintgen(n). This allows any Integer type to implement this function (e.g. BigInt). Previously, a call like rand(big(1:10)) caused a stack overflow, it is now a "no method matching" error, until randintgen(::BigInt) is implemented, possibly using a new type similar to RandIntGen.
1 parent cec2820 commit ea2bd4b

File tree

2 files changed

+15
-19
lines changed

2 files changed

+15
-19
lines changed

base/random.jl

+11-15
Original file line numberDiff line numberDiff line change
@@ -162,25 +162,24 @@ maxmultiple(k::Uint128) = div(typemax(Uint128), k + (k == 0))*k - 1
162162
maxmultiplemix(k::Uint64) = itrunc(Uint64, div((k >> 32 != 0)*0x0000000000000000FFFFFFFF00000000 + 0x0000000100000000, k + (k == 0))*k - 1)
163163

164164
immutable RandIntGen{T<:Integer, U<:Unsigned}
165-
a::T # first element of the range
166-
k::U # range length or zero for full range
165+
k::U # range length (or zero for full range)
167166
u::U # rejection threshold
168167
end
169168
# generators with 32, 128 bits entropy
170-
RandIntGen{T, U<:Union(Uint32, Uint128)}(a::T, k::U) = RandIntGen{T, U}(a, k, maxmultiple(k))
169+
RandIntGen{U<:Union(Uint32, Uint128)}(T::Type, k::U) = RandIntGen{T, U}(k, maxmultiple(k))
171170
# mixed 32/64 bits entropy generator
172-
RandIntGen{T}(a::T, k::Uint64) = RandIntGen{T,Uint64}(a, k, maxmultiplemix(k))
171+
RandIntGen(T::Type, k::Uint64) = RandIntGen{T,Uint64}(k, maxmultiplemix(k))
173172

174173

175-
# generator for ranges
176-
RandIntGen{T<:Unsigned}(r::UnitRange{T}) = isempty(r) ? error("range must be non-empty") : RandIntGen(first(r), last(r) - first(r) + one(T))
177-
174+
# generator API
175+
# randintgen(k) returns a helper object for generating random integers in the range 1:k
176+
randintgen{T<:Unsigned}(k::T) = k<1 ? error("range must be non-empty") : RandIntGen(T, k)
178177
# specialized versions
179178
for (T, U) in [(Uint8, Uint32), (Uint16, Uint32),
180179
(Int8, Uint32), (Int16, Uint32), (Int32, Uint32), (Int64, Uint64), (Int128, Uint128),
181180
(Bool, Uint32), (Char, Uint32)]
182181

183-
@eval RandIntGen(r::UnitRange{$T}) = isempty(r) ? error("range must be non-empty") : RandIntGen(first(r), convert($U, unsigned(last(r) - first(r)) + one($U))) # overflow ok
182+
@eval randintgen(k::$T) = k<1 ? error("range must be non-empty") : RandIntGen($T, convert($U, k)) # overflow ok
184183
end
185184

186185
# this function uses 32 bit entropy for small ranges of length <= typemax(Uint32) + 1
@@ -198,19 +197,18 @@ function rand{T<:Union(Uint64, Int64)}(g::RandIntGen{T,Uint64})
198197
x = rand(Uint64)
199198
end
200199
end
201-
return reinterpret(T, reinterpret(Uint64, g.a) + rem_knuth(x, g.k))
200+
return reinterpret(T, one(Uint64) + rem_knuth(x, g.k))
202201
end
203202

204203
function rand{T<:Integer, U<:Unsigned}(g::RandIntGen{T,U})
205204
x = rand(U)
206205
while x > g.u
207206
x = rand(U)
208207
end
209-
itrunc(T, unsigned(g.a) + rem_knuth(x, g.k))
208+
itrunc(T, one(U) + rem_knuth(x, g.k))
210209
end
211210

212-
rand{T<:Union(Signed,Unsigned,Bool,Char)}(r::UnitRange{T}) = rand(RandIntGen(r))
213-
rand{T}(r::Range{T}) = r[rand(1:(length(r)))]
211+
rand{T}(r::Range{T}) = r[rand(randintgen(length(r)))]
214212

215213
function rand!(g::RandIntGen, A::AbstractArray)
216214
for i = 1 : length(A)
@@ -219,10 +217,8 @@ function rand!(g::RandIntGen, A::AbstractArray)
219217
return A
220218
end
221219

222-
rand!{T<:Union(Signed,Unsigned,Bool,Char)}(r::UnitRange{T}, A::AbstractArray) = rand!(RandIntGen(r), A)
223-
224220
function rand!(r::Range, A::AbstractArray)
225-
g = RandIntGen(1:(length(r)))
221+
g = randintgen(length(r))
226222
for i = 1 : length(A)
227223
@inbounds A[i] = r[rand(g)]
228224
end

test/random.jl

+4-4
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ if sizeof(Int32) < sizeof(Int)
5454
r = rand(int32(-1):typemax(Int32))
5555
@test typeof(r) == Int32
5656
@test -1 <= r <= typemax(Int32)
57-
@test all([div(0x00010000000000000000,k)*k - 1 == Base.Random.RandIntGen(uint64(1:k)).u for k in 13 .+ int64(2).^(32:62)])
58-
@test all([div(0x00010000000000000000,k)*k - 1 == Base.Random.RandIntGen(int64(1:k)).u for k in 13 .+ int64(2).^(32:61)])
57+
@test all([div(0x00010000000000000000,k)*k - 1 == Base.Random.randintgen(uint64(k)).u for k in 13 .+ int64(2).^(32:62)])
58+
@test all([div(0x00010000000000000000,k)*k - 1 == Base.Random.randintgen(int64(k)).u for k in 13 .+ int64(2).^(32:61)])
5959

6060
end
6161

@@ -160,8 +160,8 @@ r = uint64(rand(uint32(97:122)))
160160
srand(seed)
161161
@test r == rand(uint64(97:122))
162162

163-
@test all([div(0x000100000000,k)*k - 1 == Base.Random.RandIntGen(uint64(1:k)).u for k in 13 .+ int64(2).^(1:30)])
164-
@test all([div(0x000100000000,k)*k - 1 == Base.Random.RandIntGen(int64(1:k)).u for k in 13 .+ int64(2).^(1:30)])
163+
@test all([div(0x000100000000,k)*k - 1 == Base.Random.randintgen(uint64(k)).u for k in 13 .+ int64(2).^(1:30)])
164+
@test all([div(0x000100000000,k)*k - 1 == Base.Random.randintgen(int64(k)).u for k in 13 .+ int64(2).^(1:30)])
165165

166166
import Base.Random: uuid4, UUID
167167

0 commit comments

Comments
 (0)