From beff573d68acac4867661fac07769d9c70021ecc Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Mon, 8 Sep 2014 21:02:34 +0530 Subject: [PATCH] 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. A mecanism handling not-implemented types should be 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 bf8c452112 were re-added. --- base/random.jl | 52 +++++++++++++++++++++++++++++++++++++++++++------- test/random.jl | 27 ++++++++++++++++++++------ 2 files changed, 66 insertions(+), 13 deletions(-) diff --git a/base/random.jl b/base/random.jl index 2353f2bd50e98..169af1810ada0 100644 --- a/base/random.jl +++ b/base/random.jl @@ -361,7 +361,9 @@ maxmultiple(k::UInt128) = div(typemax(UInt128), k + (k == 0))*k - 1 # maximum multiple of k within 1:2^32 or 1:2^64, depending on size maxmultiplemix(k::UInt64) = (div((k >> 32 != 0)*0x0000000000000000FFFFFFFF00000000 + 0x0000000100000000, k + (k == 0))*k - 1) % UInt64 -immutable RandIntGen{T<:Integer, U<:Unsigned} +abstract RangeGenerator + +immutable RandIntGen{T<:Integer, U<:Unsigned} <: RangeGenerator a::T # first element of the range k::U # range length or zero for full range u::U # rejection threshold @@ -373,16 +375,36 @@ RandIntGen{T}(a::T, k::UInt64) = RandIntGen{T,UInt64}(a, 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)) +inrange{T<:Unsigned}(r::UnitRange{T}) = isempty(r) ? error("range must be non-empty") : RandIntGen(first(r), last(r) - first(r) + one(T)) # specialized versions for (T, U) in [(UInt8, UInt32), (UInt16, UInt32), (Int8, UInt32), (Int16, UInt32), (Int32, UInt32), (Int64, UInt64), (Int128, UInt128), (Bool, 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 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 +end + + +# generator for BigInt +immutable RandIntGenBigInt <: RangeGenerator + a::BigInt # first + m::BigInt # range length - 1 + limbs::Array{Culong} # buffer to be copied into generated BigInt's + mask::Culong # applied to the highest limb end +function inrange(r::UnitRange{BigInt}) + m = last(r) - first(r) + m < 0 && error("range must be non-empty") + nd = ndigits(m, 2) + nlimbs, highbits = divrem(nd, 8*sizeof(Culong)) + highbits > 0 && (nlimbs += 1) + mask = highbits == 0 ? ~zero(Culong) : one(Culong)<