Skip to content

Commit 79e4104

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. This is fixed here, but a mecanism handling not-implemented types should be added. * RandIntGen is renamed to RangeGeneratorInt. * A type RangeGeneratorBigInt similar to RangeGeneratorInt (both subtypes of RangeGenerator) is introduced to handle rand on BigInt ranges. RangeGenerator is the generic constructor of such objects, taking a range as parameter. Note: two different versions are implemented depending on the GMP version (it is faster with version 6 on big ranges). * BigInt tests from commit bf8c452 are re-added.
1 parent bf57363 commit 79e4104

File tree

4 files changed

+121
-21
lines changed

4 files changed

+121
-21
lines changed

base/gmp.jl

+16-1
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,25 @@ else
1818
end
1919
typealias CdoubleMax Union(Float16, Float32, Float64)
2020

21+
const GMP_VERSION = VersionNumber(bytestring(unsafe_load(cglobal((:__gmp_version, :libgmp), Ptr{Cchar}))))
22+
const GMP_BITS_PER_LIMB = Int(unsafe_load(cglobal((:__gmp_bits_per_limb, :libgmp), Cint)))
23+
24+
# GMP's mp_limb_t is by default a typedef of `unsigned long`, but can also be configured to be either
25+
# `unsigned int` or `unsigned long long int`. The correct unsigned type is here named Limb, and must
26+
# be used whenever mp_limb_t is in the signature of ccall'ed GMP functions.
27+
if GMP_BITS_PER_LIMB == 32
28+
typealias Limb UInt32
29+
elseif GMP_BITS_PER_LIMB == 64
30+
typealias Limb UInt64
31+
else
32+
error("GMP: cannot determine the type mp_limb_t (__gmp_bits_per_limb == $GMP_BITS_PER_LIMB)")
33+
end
34+
35+
2136
type BigInt <: Integer
2237
alloc::Cint
2338
size::Cint
24-
d::Ptr{Culong}
39+
d::Ptr{Limb}
2540
function BigInt()
2641
b = new(zero(Cint), zero(Cint), C_NULL)
2742
ccall((:__gmpz_init,:libgmp), Void, (Ptr{BigInt},), &b)

base/random.jl

+80-12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
module Random
22

33
using Base.dSFMT
4+
using Base.GMP: GMP_VERSION, Limb
45

56
export srand,
67
rand, rand!,
@@ -362,31 +363,65 @@ maxmultiple(k::UInt128) = div(typemax(UInt128), k + (k == 0))*k - 1
362363
# maximum multiple of k within 1:2^32 or 1:2^64, depending on size
363364
maxmultiplemix(k::UInt64) = (div((k >> 32 != 0)*0x0000000000000000FFFFFFFF00000000 + 0x0000000100000000, k + (k == 0))*k - 1) % UInt64
364365

365-
immutable RandIntGen{T<:Integer, U<:Unsigned}
366+
abstract RangeGenerator
367+
368+
immutable RangeGeneratorInt{T<:Integer, U<:Unsigned} <: RangeGenerator
366369
a::T # first element of the range
367370
k::U # range length or zero for full range
368371
u::U # rejection threshold
369372
end
370373
# generators with 32, 128 bits entropy
371-
RandIntGen{T, U<:Union(UInt32, UInt128)}(a::T, k::U) = RandIntGen{T, U}(a, k, maxmultiple(k))
374+
RangeGeneratorInt{T, U<:Union(UInt32, UInt128)}(a::T, k::U) = RangeGeneratorInt{T, U}(a, k, maxmultiple(k))
372375
# mixed 32/64 bits entropy generator
373-
RandIntGen{T}(a::T, k::UInt64) = RandIntGen{T,UInt64}(a, k, maxmultiplemix(k))
376+
RangeGeneratorInt{T}(a::T, k::UInt64) = RangeGeneratorInt{T,UInt64}(a, k, maxmultiplemix(k))
374377

375378

376379
# generator for ranges
377-
RandIntGen{T<:Unsigned}(r::UnitRange{T}) = isempty(r) ? error("range must be non-empty") : RandIntGen(first(r), last(r) - first(r) + one(T))
380+
RangeGenerator{T<:Unsigned}(r::UnitRange{T}) = isempty(r) ? error("range must be non-empty") : RangeGeneratorInt(first(r), last(r) - first(r) + one(T))
378381

379382
# specialized versions
380383
for (T, U) in [(UInt8, UInt32), (UInt16, UInt32),
381384
(Int8, UInt32), (Int16, UInt32), (Int32, UInt32), (Int64, UInt64), (Int128, UInt128),
382385
(Bool, UInt32)]
383386

384-
@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
387+
@eval RangeGenerator(r::UnitRange{$T}) = isempty(r) ? error("range must be non-empty") : RangeGeneratorInt(first(r), convert($U, unsigned(last(r) - first(r)) + one($U))) # overflow ok
388+
end
389+
390+
if GMP_VERSION.major >= 6
391+
immutable RangeGeneratorBigInt <: RangeGenerator
392+
a::BigInt # first
393+
m::BigInt # range length - 1
394+
nlimbs::Int # number of limbs in generated BigInt's
395+
mask::Limb # applied to the highest limb
396+
end
397+
398+
else
399+
immutable RangeGeneratorBigInt <: RangeGenerator
400+
a::BigInt # first
401+
m::BigInt # range length - 1
402+
limbs::Array{Limb} # buffer to be copied into generated BigInt's
403+
mask::Limb # applied to the highest limb
404+
405+
RangeGeneratorBigInt(a, m, nlimbs, mask) = new(a, m, Array(Limb, nlimbs), mask)
406+
end
407+
end
408+
409+
410+
411+
function RangeGenerator(r::UnitRange{BigInt})
412+
m = last(r) - first(r)
413+
m < 0 && error("range must be non-empty")
414+
nd = ndigits(m, 2)
415+
nlimbs, highbits = divrem(nd, 8*sizeof(Limb))
416+
highbits > 0 && (nlimbs += 1)
417+
mask = highbits == 0 ? ~zero(Limb) : one(Limb)<<highbits - one(Limb)
418+
return RangeGeneratorBigInt(first(r), m, nlimbs, mask)
385419
end
386420

421+
387422
# this function uses 32 bit entropy for small ranges of length <= typemax(UInt32) + 1
388-
# RandIntGen is responsible for providing the right value of k
389-
function rand{T<:Union(UInt64, Int64)}(mt::MersenneTwister, g::RandIntGen{T,UInt64})
423+
# RangeGeneratorInt is responsible for providing the right value of k
424+
function rand{T<:Union(UInt64, Int64)}(mt::MersenneTwister, g::RangeGeneratorInt{T,UInt64})
390425
local x::UInt64
391426
if (g.k - 1) >> 32 == 0
392427
x = rand(mt, UInt32)
@@ -402,31 +437,64 @@ function rand{T<:Union(UInt64, Int64)}(mt::MersenneTwister, g::RandIntGen{T,UInt
402437
return reinterpret(T, reinterpret(UInt64, g.a) + rem_knuth(x, g.k))
403438
end
404439

405-
function rand{T<:Integer, U<:Unsigned}(mt::MersenneTwister, g::RandIntGen{T,U})
440+
function rand{T<:Integer, U<:Unsigned}(mt::MersenneTwister, g::RangeGeneratorInt{T,U})
406441
x = rand(mt, U)
407442
while x > g.u
408443
x = rand(mt, U)
409444
end
410445
(unsigned(g.a) + rem_knuth(x, g.k)) % T
411446
end
412447

413-
rand{T<:Union(Signed,Unsigned,Bool,Char)}(mt::MersenneTwister, r::UnitRange{T}) = rand(mt, RandIntGen(r))
448+
if GMP_VERSION.major >= 6
449+
# mpz_limbs_write and mpz_limbs_finish are available only in GMP version 6
450+
function rand(rng::AbstractRNG, g::RangeGeneratorBigInt)
451+
x = BigInt()
452+
while true
453+
# note: on CRAY computers, the second argument may be of type Cint (48 bits) and not Clong
454+
xd = ccall((:__gmpz_limbs_write, :libgmp), Ptr{Limb}, (Ptr{BigInt}, Clong), &x, g.nlimbs)
455+
limbs = pointer_to_array(xd, g.nlimbs)
456+
rand!(rng, limbs)
457+
limbs[end] &= g.mask
458+
ccall((:__gmpz_limbs_finish, :libgmp), Void, (Ptr{BigInt}, Clong), &x, g.nlimbs)
459+
x <= g.m && break
460+
end
461+
ccall((:__gmpz_add, :libgmp), Void, (Ptr{BigInt}, Ptr{BigInt}, Ptr{BigInt}), &x, &x, &g.a)
462+
return x
463+
end
464+
else
465+
function rand(rng::AbstractRNG, g::RangeGeneratorBigInt)
466+
x = BigInt()
467+
while true
468+
rand!(rng, g.limbs)
469+
g.limbs[end] &= g.mask
470+
ccall((:__gmpz_import, :libgmp), Void,
471+
(Ptr{BigInt}, Csize_t, Cint, Csize_t, Cint, Csize_t, Ptr{Void}),
472+
&x, length(g.limbs), -1, sizeof(Limb), 0, 0, &g.limbs)
473+
x <= g.m && break
474+
end
475+
ccall((:__gmpz_add, :libgmp), Void, (Ptr{BigInt}, Ptr{BigInt}, Ptr{BigInt}), &x, &x, &g.a)
476+
return x
477+
end
478+
end
479+
480+
rand{T<:Union(Signed,Unsigned,BigInt,Bool,Char)}(mt::MersenneTwister, r::UnitRange{T}) = rand(mt, RangeGenerator(r))
481+
414482

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

419-
function rand!(mt::MersenneTwister, A::AbstractArray, g::RandIntGen)
487+
function rand!(mt::MersenneTwister, A::AbstractArray, g::RangeGenerator)
420488
for i = 1 : length(A)
421489
@inbounds A[i] = rand(mt, g)
422490
end
423491
return A
424492
end
425493

426-
rand!{T<:Union(Signed,Unsigned,Bool,Char)}(mt::MersenneTwister, A::AbstractArray, r::UnitRange{T}) = rand!(mt, A, RandIntGen(r))
494+
rand!{T<:Union(Signed,Unsigned,BigInt,Bool,Char)}(mt::MersenneTwister, A::AbstractArray, r::UnitRange{T}) = rand!(mt, A, RangeGenerator(r))
427495

428496
function rand!(mt::MersenneTwister, A::AbstractArray, r::AbstractArray)
429-
g = RandIntGen(1:(length(r)))
497+
g = RangeGenerator(1:(length(r)))
430498
for i = 1 : length(A)
431499
@inbounds A[i] = r[rand(mt, g)]
432500
end

base/sysimg.jl

+4-2
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,9 @@ include("combinatorics.jl")
176176
include("rounding.jl")
177177
importall .Rounding
178178

179+
# version
180+
include("version.jl")
181+
179182
# BigInts and BigFloats
180183
include("gmp.jl")
181184
importall .GMP
@@ -198,8 +201,7 @@ include("darray.jl")
198201
include("mmap.jl")
199202
include("sharedarray.jl")
200203

201-
# utilities - version, timing, help, edit, metaprogramming
202-
include("version.jl")
204+
# utilities - timing, help, edit, metaprogramming
203205
include("datafmt.jl")
204206
importall .DataFmt
205207
include("deepcopy.jl")

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.RangeGenerator(uint64(1:k)).u for k in 13 .+ int64(2).^(32:62)])
79+
@test all([div(0x00010000000000000000,k)*k - 1 == Base.Random.RangeGenerator(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()
8499
randn(100000)
85100
randn!(Array(Float64, 100000))
@@ -190,8 +205,8 @@ r = uint64(rand(uint32(97:122)))
190205
srand(seed)
191206
@test r == rand(uint64(97:122))
192207

193-
@test all([div(0x000100000000,k)*k - 1 == Base.Random.RandIntGen(uint64(1:k)).u for k in 13 .+ int64(2).^(1:30)])
194-
@test all([div(0x000100000000,k)*k - 1 == Base.Random.RandIntGen(int64(1:k)).u for k in 13 .+ int64(2).^(1:30)])
208+
@test all([div(0x000100000000,k)*k - 1 == Base.Random.RangeGenerator(uint64(1:k)).u for k in 13 .+ int64(2).^(1:30)])
209+
@test all([div(0x000100000000,k)*k - 1 == Base.Random.RangeGenerator(int64(1:k)).u for k in 13 .+ int64(2).^(1:30)])
195210

196211
import Base.Random: uuid4, UUID
197212

0 commit comments

Comments
 (0)