From ea2bd4b9d5fa3880f461f6ceac1e559c5fd8b6af Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Wed, 10 Sep 2014 12:42:51 +0530 Subject: [PATCH] restrict RandIntGen to generate random numbers in the interval [1,n] This is based on @ivarne idea (cf. https://github.com/JuliaLang/julia/pull/8255#issuecomment-54988949), 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. --- base/random.jl | 26 +++++++++++--------------- test/random.jl | 8 ++++---- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/base/random.jl b/base/random.jl index ae199e570bf84..650c49b9f5996 100644 --- a/base/random.jl +++ b/base/random.jl @@ -162,25 +162,24 @@ maxmultiple(k::Uint128) = div(typemax(Uint128), k + (k == 0))*k - 1 maxmultiplemix(k::Uint64) = itrunc(Uint64, div((k >> 32 != 0)*0x0000000000000000FFFFFFFF00000000 + 0x0000000100000000, k + (k == 0))*k - 1) immutable RandIntGen{T<:Integer, U<:Unsigned} - a::T # first element of the range - k::U # range length or zero for full range + k::U # range length (or zero for full range) u::U # rejection threshold end # generators with 32, 128 bits entropy -RandIntGen{T, U<:Union(Uint32, Uint128)}(a::T, k::U) = RandIntGen{T, U}(a, k, maxmultiple(k)) +RandIntGen{U<:Union(Uint32, Uint128)}(T::Type, k::U) = RandIntGen{T, U}(k, maxmultiple(k)) # mixed 32/64 bits entropy generator -RandIntGen{T}(a::T, k::Uint64) = RandIntGen{T,Uint64}(a, k, maxmultiplemix(k)) +RandIntGen(T::Type, k::Uint64) = RandIntGen{T,Uint64}(k, maxmultiplemix(k)) -# generator for ranges -RandIntGen{T<:Unsigned}(r::UnitRange{T}) = isempty(r) ? error("range must be non-empty") : RandIntGen(first(r), last(r) - first(r) + one(T)) - +# generator API +# randintgen(k) returns a helper object for generating random integers in the range 1:k +randintgen{T<:Unsigned}(k::T) = k<1 ? error("range must be non-empty") : RandIntGen(T, k) # specialized versions for (T, U) in [(Uint8, Uint32), (Uint16, Uint32), (Int8, Uint32), (Int16, Uint32), (Int32, Uint32), (Int64, Uint64), (Int128, Uint128), (Bool, Uint32), (Char, Uint32)] - @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 + @eval randintgen(k::$T) = k<1 ? error("range must be non-empty") : RandIntGen($T, convert($U, k)) # overflow ok end # this function uses 32 bit entropy for small ranges of length <= typemax(Uint32) + 1 @@ -198,7 +197,7 @@ function rand{T<:Union(Uint64, Int64)}(g::RandIntGen{T,Uint64}) x = rand(Uint64) end end - return reinterpret(T, reinterpret(Uint64, g.a) + rem_knuth(x, g.k)) + return reinterpret(T, one(Uint64) + rem_knuth(x, g.k)) end function rand{T<:Integer, U<:Unsigned}(g::RandIntGen{T,U}) @@ -206,11 +205,10 @@ function rand{T<:Integer, U<:Unsigned}(g::RandIntGen{T,U}) while x > g.u x = rand(U) end - itrunc(T, unsigned(g.a) + rem_knuth(x, g.k)) + itrunc(T, one(U) + rem_knuth(x, g.k)) end -rand{T<:Union(Signed,Unsigned,Bool,Char)}(r::UnitRange{T}) = rand(RandIntGen(r)) -rand{T}(r::Range{T}) = r[rand(1:(length(r)))] +rand{T}(r::Range{T}) = r[rand(randintgen(length(r)))] function rand!(g::RandIntGen, A::AbstractArray) for i = 1 : length(A) @@ -219,10 +217,8 @@ function rand!(g::RandIntGen, A::AbstractArray) return A end -rand!{T<:Union(Signed,Unsigned,Bool,Char)}(r::UnitRange{T}, A::AbstractArray) = rand!(RandIntGen(r), A) - function rand!(r::Range, A::AbstractArray) - g = RandIntGen(1:(length(r))) + g = randintgen(length(r)) for i = 1 : length(A) @inbounds A[i] = r[rand(g)] end diff --git a/test/random.jl b/test/random.jl index 9e9970f208172..0024feeba9e02 100644 --- a/test/random.jl +++ b/test/random.jl @@ -54,8 +54,8 @@ if sizeof(Int32) < sizeof(Int) r = rand(int32(-1):typemax(Int32)) @test typeof(r) == Int32 @test -1 <= r <= typemax(Int32) - @test all([div(0x00010000000000000000,k)*k - 1 == Base.Random.RandIntGen(uint64(1:k)).u for k in 13 .+ int64(2).^(32:62)]) - @test all([div(0x00010000000000000000,k)*k - 1 == Base.Random.RandIntGen(int64(1:k)).u for k in 13 .+ int64(2).^(32:61)]) + @test all([div(0x00010000000000000000,k)*k - 1 == Base.Random.randintgen(uint64(k)).u for k in 13 .+ int64(2).^(32:62)]) + @test all([div(0x00010000000000000000,k)*k - 1 == Base.Random.randintgen(int64(k)).u for k in 13 .+ int64(2).^(32:61)]) end @@ -160,8 +160,8 @@ r = uint64(rand(uint32(97:122))) srand(seed) @test r == rand(uint64(97:122)) -@test all([div(0x000100000000,k)*k - 1 == Base.Random.RandIntGen(uint64(1:k)).u for k in 13 .+ int64(2).^(1:30)]) -@test all([div(0x000100000000,k)*k - 1 == Base.Random.RandIntGen(int64(1:k)).u for k in 13 .+ int64(2).^(1:30)]) +@test all([div(0x000100000000,k)*k - 1 == Base.Random.randintgen(uint64(k)).u for k in 13 .+ int64(2).^(1:30)]) +@test all([div(0x000100000000,k)*k - 1 == Base.Random.randintgen(int64(k)).u for k in 13 .+ int64(2).^(1:30)]) import Base.Random: uuid4, UUID