Skip to content

Commit 21a73b9

Browse files
committed
refactor rand on UnitRange / AbstractArray
This is another approach to the (non-merged) refactoring part of #8309 and #8649. The goal is to reduce to the minimum the different code paths taken by UnitRange and AbstractArray (UnitRange ranges are handled differently so that those with overflowing length still work). In particular two rand! method are merged into one.
1 parent f2b5a11 commit 21a73b9

File tree

1 file changed

+51
-73
lines changed

1 file changed

+51
-73
lines changed

base/random.jl

+51-73
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module Random
22

3-
using Base.dSFMT
3+
using Base: dSFMT, IntTypes
44
using Base.GMP: GMP_VERSION, Limb
55

66
export srand,
@@ -361,97 +361,85 @@ rem_knuth{T<:Unsigned}(a::T, b::T) = b != 0 ? a % b : a
361361
# maximum multiple of k <= 2^bits(T) decremented by one,
362362
# that is 0xFFFFFFFF if k = typemax(T) - typemin(T) with intentional underflow
363363
maxmultiple(k::UInt32) = (div(0x0000000100000000,k + (k == 0))*k - 1) % UInt32
364-
maxmultiple(k::UInt64) = (div(0x00000000000000010000000000000000, k + (k == 0))*k - 1) % UInt64
364+
365+
maxmultiple64(k::UInt64) = (div(0x00000000000000010000000000000000, k + (k == 0))*k - 1) % UInt64
366+
367+
# maximum multiple of k within 1:2^32 or 1:2^64, depending on size
368+
maxmultiple(k::UInt64) = (div((k >> 32 != 0)*0x0000000000000000FFFFFFFF00000000 + 0x0000000100000000, k + (k == 0))*k - 1) % UInt64
369+
365370
# maximum multiple of k within 1:typemax(UInt128)
366371
maxmultiple(k::UInt128) = div(typemax(UInt128), k + (k == 0))*k - 1
367-
# maximum multiple of k within 1:2^32 or 1:2^64, depending on size
368-
maxmultiplemix(k::UInt64) = (div((k >> 32 != 0)*0x0000000000000000FFFFFFFF00000000 + 0x0000000100000000, k + (k == 0))*k - 1) % UInt64
369372

370-
abstract RangeGenerator
373+
# utility to generate random numbers in a range
374+
abstract RangeGenerator{T<:Integer}
371375

372-
immutable RangeGeneratorInt{T<:Integer, U<:Unsigned} <: RangeGenerator
373-
a::T # first element of the range
376+
immutable RangeGeneratorInt{T<:Integer, U<:Union(UInt32, UInt64, UInt128)} <: RangeGenerator{T}
374377
k::U # range length or zero for full range
375378
u::U # rejection threshold
376379
end
377-
# generators with 32, 128 bits entropy
378-
RangeGeneratorInt{T, U<:Union(UInt32, UInt128)}(a::T, k::U) = RangeGeneratorInt{T, U}(a, k, maxmultiple(k))
379-
# mixed 32/64 bits entropy generator
380-
RangeGeneratorInt{T}(a::T, k::UInt64) = RangeGeneratorInt{T,UInt64}(a, k, maxmultiplemix(k))
381380

382-
383-
# generator for ranges
384-
RangeGenerator{T<:Unsigned}(r::UnitRange{T}) = isempty(r) ? error("range must be non-empty") : RangeGeneratorInt(first(r), last(r) - first(r) + one(T))
385-
386-
# specialized versions
387-
for (T, U) in [(UInt8, UInt32), (UInt16, UInt32),
388-
(Int8, UInt32), (Int16, UInt32), (Int32, UInt32), (Int64, UInt64), (Int128, UInt128),
389-
(Bool, UInt32)]
390-
391-
@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
392-
end
381+
@inline RangeGenerator{T, U}(::Type{T}, m::U) = (k=m+one(U); RangeGeneratorInt{T, U}(k, maxmultiple(k)))
393382

394383
if GMP_VERSION.major >= 6
395-
immutable RangeGeneratorBigInt <: RangeGenerator
396-
a::BigInt # first
384+
immutable RangeGeneratorBigInt <: RangeGenerator{BigInt}
397385
m::BigInt # range length - 1
398386
nlimbs::Int # number of limbs in generated BigInt's
399387
mask::Limb # applied to the highest limb
400388
end
401389

402390
else
403-
immutable RangeGeneratorBigInt <: RangeGenerator
404-
a::BigInt # first
391+
immutable RangeGeneratorBigInt <: RangeGenerator{BigInt}
405392
m::BigInt # range length - 1
406393
limbs::Vector{Limb} # buffer to be copied into generated BigInt's
407394
mask::Limb # applied to the highest limb
408395

409-
RangeGeneratorBigInt(a, m, nlimbs, mask) = new(a, m, Array(Limb, nlimbs), mask)
396+
RangeGeneratorBigInt(m, nlimbs, mask) = new(m, Array(Limb, nlimbs), mask)
410397
end
411398
end
412399

413-
414-
415-
function RangeGenerator(r::UnitRange{BigInt})
416-
m = last(r) - first(r)
417-
m < 0 && error("range must be non-empty")
400+
function RangeGenerator(::Type{BigInt}, m::BigInt)
418401
nd = ndigits(m, 2)
419402
nlimbs, highbits = divrem(nd, 8*sizeof(Limb))
420403
highbits > 0 && (nlimbs += 1)
421404
mask = highbits == 0 ? ~zero(Limb) : one(Limb)<<highbits - one(Limb)
422-
return RangeGeneratorBigInt(first(r), m, nlimbs, mask)
405+
return RangeGeneratorBigInt(m, nlimbs, mask)
423406
end
424407

408+
# RangeGenerator dispatch
425409

426-
# this function uses 32 bit entropy for small ranges of length <= typemax(UInt32) + 1
427-
# RangeGeneratorInt is responsible for providing the right value of k
428-
function rand{T<:Union(UInt64, Int64)}(rng::AbstractRNG, g::RangeGeneratorInt{T,UInt64})
429-
local x::UInt64
430-
if (g.k - 1) >> 32 == 0
431-
x = rand(rng, UInt32)
432-
while x > g.u
433-
x = rand(rng, UInt32)
434-
end
435-
else
436-
x = rand(rng, UInt64)
437-
while x > g.u
438-
x = rand(rng, UInt64)
439-
end
440-
end
441-
return reinterpret(T, reinterpret(UInt64, g.a) + rem_knuth(x, g.k))
442-
end
410+
@inline checknonempty(r) = isempty(r) && error("collection must be non-empty")
411+
@inline RangeGenerator(r::AbstractArray) = (checknonempty(r); n = length(r); RangeGenerator(n-one(n)))
412+
@inline RangeGenerator{T<:Union(IntTypes...,BigInt)}(r::UnitRange{T}) = (checknonempty(r); RangeGenerator(last(r)-first(r)))
413+
414+
@inline RangeGenerator{T<:Union(UInt32,UInt64,UInt128,BigInt)}(m::T) = RangeGenerator(T, m)
415+
@inline RangeGenerator{T<:Union(Int32,Int64,Int128)}(m::T) = RangeGenerator(T, unsigned(m))
416+
@inline RangeGenerator{T<:Union(Int8,UInt8,Int16,UInt16)}(m::T) = RangeGenerator(T, m % UInt32)
417+
418+
# rand on RangeGenerator
443419

444-
function rand{T<:Integer, U<:Unsigned}(rng::AbstractRNG, g::RangeGeneratorInt{T,U})
445-
x = rand(rng, U)
446-
while x > g.u
420+
@inline rand{T}(rng::AbstractRNG, g::RangeGenerator{T}) = rand(rng, g, one(T))
421+
422+
@inline function rand_lessthan{U}(rng::AbstractRNG, u::U)
423+
while true
447424
x = rand(rng, U)
425+
x <= u && return x
448426
end
449-
(unsigned(g.a) + rem_knuth(x, g.k)) % T
450427
end
451428

429+
# this function uses 32 bit entropy for small ranges of length <= typemax(UInt32) + 1
430+
# RangeGeneratorInt is responsible for providing the right value of k
431+
@inline rand{T}(rng::AbstractRNG, g::RangeGeneratorInt{T,UInt64}, start::T) =
432+
(g.k - 1) >> 32 == 0 ?
433+
start + rand_lessthan(rng, g.u % UInt32) % UInt64 % g.k % T :
434+
rand(rng, g, start, true)
435+
436+
@inline rand{T}(rng::AbstractRNG, g::RangeGeneratorInt{T}, start::T, dummy=true) =
437+
start + rem_knuth(rand_lessthan(rng, g.u), g.k) % T
438+
439+
452440
if GMP_VERSION.major >= 6
453441
# mpz_limbs_write and mpz_limbs_finish are available only in GMP version 6
454-
function rand(rng::AbstractRNG, g::RangeGeneratorBigInt)
442+
function rand(rng::AbstractRNG, g::RangeGeneratorBigInt, start::BigInt)
455443
x = BigInt()
456444
while true
457445
# note: on CRAY computers, the second argument may be of type Cint (48 bits) and not Clong
@@ -462,11 +450,11 @@ if GMP_VERSION.major >= 6
462450
ccall((:__gmpz_limbs_finish, :libgmp), Void, (Ptr{BigInt}, Clong), &x, g.nlimbs)
463451
x <= g.m && break
464452
end
465-
ccall((:__gmpz_add, :libgmp), Void, (Ptr{BigInt}, Ptr{BigInt}, Ptr{BigInt}), &x, &x, &g.a)
453+
ccall((:__gmpz_add, :libgmp), Void, (Ptr{BigInt}, Ptr{BigInt}, Ptr{BigInt}), &x, &x, &start)
466454
return x
467455
end
468456
else
469-
function rand(rng::AbstractRNG, g::RangeGeneratorBigInt)
457+
function rand(rng::AbstractRNG, g::RangeGeneratorBigInt, start::BigInt)
470458
x = BigInt()
471459
while true
472460
rand!(rng, g.limbs)
@@ -476,31 +464,21 @@ else
476464
&x, length(g.limbs), -1, sizeof(Limb), 0, 0, &g.limbs)
477465
x <= g.m && break
478466
end
479-
ccall((:__gmpz_add, :libgmp), Void, (Ptr{BigInt}, Ptr{BigInt}, Ptr{BigInt}), &x, &x, &g.a)
467+
ccall((:__gmpz_add, :libgmp), Void, (Ptr{BigInt}, Ptr{BigInt}, Ptr{BigInt}), &x, &x, &start)
480468
return x
481469
end
482470
end
483471

484-
rand{T<:Union(Signed,Unsigned,BigInt,Bool,Char)}(rng::AbstractRNG, r::UnitRange{T}) = rand(rng, RangeGenerator(r))
485-
486-
487472
# Randomly draw a sample from an AbstractArray r
488473
# (e.g. r is a range 0:2:8 or a vector [2, 3, 5, 7])
489-
rand(rng::AbstractRNG, r::AbstractArray) = @inbounds return r[rand(rng, 1:length(r))]
490-
491-
function rand!(rng::AbstractRNG, A::AbstractArray, g::RangeGenerator)
492-
for i = 1 : length(A)
493-
@inbounds A[i] = rand(rng, g)
494-
end
495-
return A
496-
end
474+
@inline rand(rng::AbstractRNG, r::AbstractArray) = rand(rng, r, RangeGenerator(r))
497475

498-
rand!{T<:Union(Signed,Unsigned,BigInt,Bool,Char)}(rng::AbstractRNG, A::AbstractArray, r::UnitRange{T}) = rand!(rng, A, RangeGenerator(r))
476+
@inline rand(rng::AbstractRNG, r::AbstractArray, g::RangeGenerator) = @inbounds return r[rand(rng, g)]
477+
@inline rand{T<:Union(IntTypes...,BigInt)}(rng::AbstractRNG, r::UnitRange{T}, g::RangeGenerator) = rand(rng, g, first(r))
499478

500-
function rand!(rng::AbstractRNG, A::AbstractArray, r::AbstractArray)
501-
g = RangeGenerator(1:(length(r)))
479+
function rand!(rng::AbstractRNG, A::AbstractArray, r::AbstractArray, g::RangeGenerator=RangeGenerator(r))
502480
for i = 1 : length(A)
503-
@inbounds A[i] = r[rand(rng, g)]
481+
@inbounds A[i] = rand(rng, r, g)
504482
end
505483
return A
506484
end

0 commit comments

Comments
 (0)