Skip to content

Commit 3f19fc1

Browse files
committed
implement rand(::Range{BigInt})
* Previously, calling e.g. rand(big(1:4)) caused a stack overflow, because rand(mt::MersenneTwister, r::AbstractArray) was calling itself recursively. So a a method handling not-implemented types has been added. * A type RandIntGenBigInt similar to RandIntGen (both subtypes of RangeGenerator) has been introduced to handle rand on big ranges. The generic function to construct such objects is named inrange. Note: mpz_import could be replaced by mpz_limbs_{write,finish} when GMP version 6 is used. * BigInt tests from commit bf8c452 were re-added.
1 parent bd365ca commit 3f19fc1

File tree

2 files changed

+68
-13
lines changed

2 files changed

+68
-13
lines changed

base/random.jl

+47-7
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,9 @@ maxmultiple(k::UInt128) = div(typemax(UInt128), k + (k == 0))*k - 1
361361
# maximum multiple of k within 1:2^32 or 1:2^64, depending on size
362362
maxmultiplemix(k::UInt64) = (div((k >> 32 != 0)*0x0000000000000000FFFFFFFF00000000 + 0x0000000100000000, k + (k == 0))*k - 1) % UInt64
363363

364-
immutable RandIntGen{T<:Integer, U<:Unsigned}
364+
abstract RangeGenerator
365+
366+
immutable RandIntGen{T<:Integer, U<:Unsigned} <: RangeGenerator
365367
a::T # first element of the range
366368
k::U # range length or zero for full range
367369
u::U # rejection threshold
@@ -373,16 +375,36 @@ RandIntGen{T}(a::T, k::UInt64) = RandIntGen{T,UInt64}(a, k, maxmultiplemix(k))
373375

374376

375377
# generator for ranges
376-
RandIntGen{T<:Unsigned}(r::UnitRange{T}) = isempty(r) ? error("range must be non-empty") : RandIntGen(first(r), last(r) - first(r) + one(T))
378+
inrange{T<:Unsigned}(r::UnitRange{T}) = isempty(r) ? error("range must be non-empty") : RandIntGen(first(r), last(r) - first(r) + one(T))
377379

378380
# specialized versions
379381
for (T, U) in [(UInt8, UInt32), (UInt16, UInt32),
380382
(Int8, UInt32), (Int16, UInt32), (Int32, UInt32), (Int64, UInt64), (Int128, UInt128),
381383
(Bool, UInt32)]
382384

383-
@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
385+
@eval inrange(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
386+
end
387+
388+
389+
# generator for BigInt
390+
immutable RandIntGenBigInt <: RangeGenerator
391+
a::BigInt # first
392+
m::BigInt # range length - 1
393+
limbs::Array{Culong} # buffer to be copied into generated BigInt's
394+
mask::Culong # applied to the highest limb
384395
end
385396

397+
function inrange(r::UnitRange{BigInt})
398+
m = last(r) - first(r)
399+
m < 0 && error("range must be non-empty")
400+
nd = ndigits(m, 2)
401+
nlimbs, highbits = divrem(nd, 8*sizeof(Culong))
402+
highbits > 0 && (nlimbs += 1)
403+
mask = highbits == 0 ? ~zero(Culong) : one(Culong)<<highbits - one(Culong)
404+
return RandIntGenBigInt(first(r), m, Array(Culong, nlimbs), mask)
405+
end
406+
407+
386408
# this function uses 32 bit entropy for small ranges of length <= typemax(UInt32) + 1
387409
# RandIntGen is responsible for providing the right value of k
388410
function rand{T<:Union(UInt64, Int64)}(mt::MersenneTwister, g::RandIntGen{T,UInt64})
@@ -409,23 +431,41 @@ function rand{T<:Integer, U<:Unsigned}(mt::MersenneTwister, g::RandIntGen{T,U})
409431
(unsigned(g.a) + rem_knuth(x, g.k)) % T
410432
end
411433

412-
rand{T<:Union(Signed,Unsigned,Bool,Char)}(mt::MersenneTwister, r::UnitRange{T}) = rand(mt, RandIntGen(r))
434+
function rand(rng::AbstractRNG, g::RandIntGenBigInt)
435+
x = BigInt()
436+
while true
437+
rand!(rng, g.limbs)
438+
g.limbs[end] &= g.mask
439+
ccall((:__gmpz_import, :libgmp), Void,
440+
(Ptr{BigInt}, Csize_t, Cint, Csize_t, Cint, Csize_t, Ptr{Void}),
441+
&x, length(g.limbs), -1, sizeof(Culong), 0, 0, &g.limbs)
442+
x <= g.m && break
443+
end
444+
ccall((:__gmpz_add, :libgmp), Void, (Ptr{BigInt}, Ptr{BigInt}, Ptr{BigInt}), &x, &x, &g.a)
445+
return x
446+
end
447+
448+
449+
rand{T<:Union(Signed,Unsigned,BigInt,Bool,Char)}(mt::MersenneTwister, r::UnitRange{T}) = rand(mt, inrange(r))
450+
451+
rand{T}(mt::MersenneTwister, r::UnitRange{T}) = error("there is no random generator for ranges of type $T")
452+
413453

414454
# Randomly draw a sample from an AbstractArray r
415455
# (e.g. r is a range 0:2:8 or a vector [2, 3, 5, 7])
416456
rand(mt::MersenneTwister, r::AbstractArray) = @inbounds return r[rand(mt, 1:length(r))]
417457

418-
function rand!(mt::MersenneTwister, A::AbstractArray, g::RandIntGen)
458+
function rand!(mt::MersenneTwister, A::AbstractArray, g::RangeGenerator)
419459
for i = 1 : length(A)
420460
@inbounds A[i] = rand(mt, g)
421461
end
422462
return A
423463
end
424464

425-
rand!{T<:Union(Signed,Unsigned,Bool,Char)}(mt::MersenneTwister, A::AbstractArray, r::UnitRange{T}) = rand!(mt, A, RandIntGen(r))
465+
rand!{T<:Union(Signed,Unsigned,BigInt,Bool,Char)}(mt::MersenneTwister, A::AbstractArray, r::UnitRange{T}) = rand!(mt, A, inrange(r))
426466

427467
function rand!(mt::MersenneTwister, A::AbstractArray, r::AbstractArray)
428-
g = RandIntGen(1:(length(r)))
468+
g = inrange(1:(length(r)))
429469
for i = 1 : length(A)
430470
@inbounds A[i] = r[rand(mt, g)]
431471
end

test/random.jl

+21-6
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ randn!(MersenneTwister(42), A)
5454
@test A == [-0.5560268761463861 0.027155338009193845;
5555
-0.444383357109696 -0.29948409035891055]
5656

57-
for T in (Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128,
57+
for T in (Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128, BigInt,
5858
Float16, Float32, Float64, Rational{Int})
5959
r = rand(convert(T, 97):convert(T, 122))
6060
@test typeof(r) == T
@@ -64,7 +64,7 @@ for T in (Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt
6464
@test 97 <= r <= 122
6565
@test mod(r,2)==1
6666

67-
if T<:Integer
67+
if T<:Integer && !(T===BigInt)
6868
x = rand(typemin(T):typemax(T))
6969
@test isa(x,T)
7070
@test typemin(T) <= x <= typemax(T)
@@ -75,11 +75,26 @@ if sizeof(Int32) < sizeof(Int)
7575
r = rand(int32(-1):typemax(Int32))
7676
@test typeof(r) == Int32
7777
@test -1 <= r <= typemax(Int32)
78-
@test all([div(0x00010000000000000000,k)*k - 1 == Base.Random.RandIntGen(uint64(1:k)).u for k in 13 .+ int64(2).^(32:62)])
79-
@test all([div(0x00010000000000000000,k)*k - 1 == Base.Random.RandIntGen(int64(1:k)).u for k in 13 .+ int64(2).^(32:61)])
78+
@test all([div(0x00010000000000000000,k)*k - 1 == Base.Random.inrange(uint64(1:k)).u for k in 13 .+ int64(2).^(32:62)])
79+
@test all([div(0x00010000000000000000,k)*k - 1 == Base.Random.inrange(int64(1:k)).u for k in 13 .+ int64(2).^(32:61)])
8080

8181
end
8282

83+
# BigInt specific
84+
for T in [UInt32, UInt64, UInt128, Int128]
85+
s = big(typemax(T)-1000) : big(typemax(T)) + 10000
86+
@test rand(s) != rand(s)
87+
@test big(typemax(T)-1000) <= rand(s) <= big(typemax(T)) + 10000
88+
r = rand(s, 1, 2)
89+
@test size(r) == (1, 2)
90+
@test typeof(r) == Matrix{BigInt}
91+
92+
srand(0)
93+
r = rand(s)
94+
srand(0)
95+
@test rand(s) == r
96+
end
97+
8398
randn(100000)
8499
randn!(Array(Float64, 100000))
85100
randn(MersenneTwister(10), 100000)
@@ -181,8 +196,8 @@ r = uint64(rand(uint32(97:122)))
181196
srand(seed)
182197
@test r == rand(uint64(97:122))
183198

184-
@test all([div(0x000100000000,k)*k - 1 == Base.Random.RandIntGen(uint64(1:k)).u for k in 13 .+ int64(2).^(1:30)])
185-
@test all([div(0x000100000000,k)*k - 1 == Base.Random.RandIntGen(int64(1:k)).u for k in 13 .+ int64(2).^(1:30)])
199+
@test all([div(0x000100000000,k)*k - 1 == Base.Random.inrange(uint64(1:k)).u for k in 13 .+ int64(2).^(1:30)])
200+
@test all([div(0x000100000000,k)*k - 1 == Base.Random.inrange(int64(1:k)).u for k in 13 .+ int64(2).^(1:30)])
186201

187202
import Base.Random: uuid4, UUID
188203

0 commit comments

Comments
 (0)