Skip to content

Commit 7191809

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 17abdc3 commit 7191809

File tree

2 files changed

+15
-18
lines changed

2 files changed

+15
-18
lines changed

base/random.jl

+11-14
Original file line numberDiff line numberDiff line change
@@ -160,24 +160,24 @@ maxmultiple(k::Uint128) = div(typemax(Uint128), k + (k == 0))*k - 1
160160
maxmultiplemix(k::Uint64) = convert(Uint64, div((k >> 32 != 0)*0x0000000000000000FFFFFFFF00000000 + 0x0000000100000000, k + (k == 0))*k - 1)
161161

162162
immutable RandIntGen{T<:Integer, U<:Unsigned}
163-
a::T # first element of the range
164-
k::U # range length or zero for full range
163+
k::U # range length (or zero for full range)
165164
u::U # rejection threshold
166165
end
167166
# generators with 32, 128 bits entropy
168-
RandIntGen{T, U<:Union(Uint32, Uint128)}(a::T, k::U) = RandIntGen{T, U}(a, k, maxmultiple(k))
167+
RandIntGen{T, U<:Union(Uint32, Uint128)}(::Type{T}, k::U) = RandIntGen{T, U}(k, maxmultiple(k))
169168
# mixed 32/64 bits entropy generator
170-
RandIntGen{T}(a::T, k::Uint64) = RandIntGen{T,Uint64}(a, k, maxmultiplemix(k))
169+
RandIntGen{T}(::Type{T}, k::Uint64) = RandIntGen{T,Uint64}(k, maxmultiplemix(k))
171170

172171

173-
# generator for ranges
174-
RandIntGen{T<:Unsigned}(r::UnitRange{T}) = isempty(r) ? error("range must be non-empty") : RandIntGen(first(r), convert(T,last(r) - first(r) + 1))
172+
# generator API
173+
# randintgen(k) returns an object generating random integers in the range 1:k
174+
randintgen{T<:Unsigned}(k::T) = k<1 ? error("range must be non-empty") : RandIntGen(T, k)
175175
# specialized versions
176176
for (T, U) in [(Uint8, Uint32), (Uint16, Uint32),
177177
(Int8, Uint32), (Int16, Uint32), (Int32, Uint32), (Int64, Uint64), (Int128, Uint128),
178178
(Bool, Uint32), (Char, Uint32)]
179179

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

183183
# this function uses 32 bit entropy for small ranges of length <= typemax(Uint32) + 1
@@ -195,19 +195,18 @@ function rand{T<:Union(Uint64, Int64)}(g::RandIntGen{T,Uint64})
195195
x = rand(Uint64)
196196
end
197197
end
198-
return convert(T, g.a + rem_knuth(x, g.k))
198+
return convert(T, 1 + rem_knuth(x, g.k))
199199
end
200200

201201
function rand{T<:Integer, U<:Unsigned}(g::RandIntGen{T,U})
202202
x = rand(U)
203203
while x > g.u
204204
x = rand(U)
205205
end
206-
convert(T, g.a + rem_knuth(x, g.k))
206+
convert(T, 1 + rem_knuth(x, g.k))
207207
end
208208

209-
rand{T<:Union(Signed,Unsigned,Bool,Char)}(r::UnitRange{T}) = rand(RandIntGen(r))
210-
rand{T}(r::Range{T}) = r[rand(1:(length(r)))]
209+
rand{T}(r::Range{T}) = r[rand(randintgen(length(r)))]
211210

212211
function rand!(g::RandIntGen, A::AbstractArray)
213212
for i = 1 : length(A)
@@ -216,10 +215,8 @@ function rand!(g::RandIntGen, A::AbstractArray)
216215
return A
217216
end
218217

219-
rand!{T<:Union(Signed,Unsigned,Bool,Char)}(r::UnitRange{T}, A::AbstractArray) = rand!(RandIntGen(r), A)
220-
221218
function rand!(r::Range, A::AbstractArray)
222-
g = RandIntGen(1:(length(r)))
219+
g = randintgen(length(r))
223220
for i = 1 : length(A)
224221
@inbounds A[i] = r[rand(g)]
225222
end

test/random.jl

+4-4
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ if sizeof(Int32) < sizeof(Int)
4848
r = rand(int32(-1):typemax(Int32))
4949
@test typeof(r) == Int32
5050
@test -1 <= r <= typemax(Int32)
51-
@test all([div(0x00010000000000000000,k)*k - 1 == Base.Random.RandIntGen(uint64(1:k)).u for k in 13 .+ int64(2).^(32:62)])
52-
@test all([div(0x00010000000000000000,k)*k - 1 == Base.Random.RandIntGen(int64(1:k)).u for k in 13 .+ int64(2).^(32:61)])
51+
@test all([div(0x00010000000000000000,k)*k - 1 == Base.Random.randintgen(uint64(k)).u for k in 13 .+ int64(2).^(32:62)])
52+
@test all([div(0x00010000000000000000,k)*k - 1 == Base.Random.randintgen(int64(k)).u for k in 13 .+ int64(2).^(32:61)])
5353

5454
end
5555

@@ -154,8 +154,8 @@ r = uint64(rand(uint32(97:122)))
154154
srand(seed)
155155
@test r == rand(uint64(97:122))
156156

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

160160
import Base.Random: uuid4, UUID
161161

0 commit comments

Comments
 (0)