• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JuliaLang / julia / #37939

21 Oct 2024 06:01AM UTC coverage: 87.654% (+0.009%) from 87.645%
#37939

push

local

web-flow
Reroute` (Upper/Lower)Triangular * Diagonal` through `__muldiag` (#55984)

Currently, `::Diagonal * ::AbstractMatrix` calls the method
`LinearAlgebra.__muldiag!` in general that scales the rows, and
similarly for the diagonal on the right. The implementation of
`__muldiag` was duplicating the logic in `LinearAlgebra.modify!` and the
methods for `MulAddMul`. This PR replaces the various branches with
calls to `modify!` instead. I've also extracted the multiplication logic
into its own function `__muldiag_nonzeroalpha!` so that this may be
specialized for matrix types, such as triangular ones.

Secondly, `::Diagonal * ::UpperTriangular` (and similarly, other
triangular matrices) was specialized to forward the multiplication to
the parent of the triangular. For strided matrices, however, it makes
more sense to use the structure and scale only the filled half of the
matrix. Firstly, this improves performance, and secondly, this avoids
errors in case the parent isn't fully initialized corresponding to the
structural zero elements.

Performance improvement:
```julia
julia> D = Diagonal(1:400);

julia> U = UpperTriangular(zeros(size(D)));

julia> @btime $D * $U;
  314.944 μs (3 allocations: 1.22 MiB) # v"1.12.0-DEV.1288"
  195.960 μs (3 allocations: 1.22 MiB) # This PR
```
Fix:
```julia
julia> M = Matrix{BigFloat}(undef, 2, 2);

julia> M[1,1] = M[2,2] = M[1,2] = 3;

julia> U = UpperTriangular(M)
2×2 UpperTriangular{BigFloat, Matrix{BigFloat}}:
 3.0  3.0
  ⋅   3.0

julia> D = Diagonal(1:2);

julia> U * D # works after this PR
2×2 UpperTriangular{BigFloat, Matrix{BigFloat}}:
 3.0  6.0
  ⋅   6.0
```

63 of 63 new or added lines in 2 files covered. (100.0%)

80 existing lines in 7 files now uncovered.

79179 of 90331 relevant lines covered (87.65%)

17146814.57 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.91
/stdlib/Random/src/generation.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
# Uniform random generation
4

5
# This file contains the creation of Sampler objects and the associated generation of
6
# random values from them. More specifically, given the specification S of a set
7
# of values to pick from (e.g. 1:10, or "a string"), we define
8
#
9
# 1) Sampler(rng, S, ::Repetition) -> sampler
10
# 2) rand(rng, sampler) -> random value
11
#
12
# Note that the 1) is automated when the sampler is not intended to carry information,
13
# i.e. the default fall-backs SamplerType and SamplerTrivial are used.
14

15
## from types: rand(::Type, [dims...])
16

17
### random floats
18

19
Sampler(::Type{RNG}, ::Type{T}, n::Repetition) where {RNG<:AbstractRNG,T<:AbstractFloat} =
46,645,336✔
20
    Sampler(RNG, CloseOpen01(T), n)
21

22
# generic random generation function which can be used by RNG implementers
23
# it is not defined as a fallback rand method as this could create ambiguities
24

25
rand(r::AbstractRNG, ::SamplerTrivial{CloseOpen01{Float16}}) =
6,703✔
26
    Float16(reinterpret(Float32,
27
                        (rand(r, UInt10(UInt32)) << 13)  | 0x3f800000) - 1)
28

29
rand(r::AbstractRNG, ::SamplerTrivial{CloseOpen01{Float32}}) =
6,647✔
30
    reinterpret(Float32, rand(r, UInt23()) | 0x3f800000) - 1
31

32
rand(r::AbstractRNG, ::SamplerTrivial{CloseOpen12_64}) =
44,895✔
33
    reinterpret(Float64, 0x3ff0000000000000 | rand(r, UInt52()))
34

35
rand(r::AbstractRNG, ::SamplerTrivial{CloseOpen01_64}) = rand(r, CloseOpen12()) - 1.0
434,829✔
36

37
#### BigFloat
38

39
const bits_in_Limb = sizeof(Limb) << 3
40
const Limb_high_bit = one(Limb) << (bits_in_Limb-1)
41

42
struct SamplerBigFloat{I<:FloatInterval{BigFloat}} <: Sampler{BigFloat}
43
    prec::Int
44
    nlimbs::Int
45
    limbs::Vector{Limb}
46
    shift::UInt
47

48
    function SamplerBigFloat{I}(prec::Int) where I<:FloatInterval{BigFloat}
49
        nlimbs = (prec-1) ÷ bits_in_Limb + 1
1,518,875✔
50
        limbs = Vector{Limb}(undef, nlimbs)
3,037,750✔
51
        shift = nlimbs * bits_in_Limb - prec
1,518,875✔
52
        new(prec, nlimbs, limbs, shift)
1,518,875✔
53
    end
54
end
55

56
Sampler(::Type{<:AbstractRNG}, I::FloatInterval{BigFloat}, ::Repetition) =
1,518,875✔
57
    SamplerBigFloat{typeof(I)}(precision(BigFloat))
58

59
function _rand!(rng::AbstractRNG, z::BigFloat, sp::SamplerBigFloat)
1,560,565✔
60
    precision(z) == sp.prec || throw(ArgumentError("incompatible BigFloat precision"))
1,560,567✔
61
    limbs = sp.limbs
1,560,563✔
62
    rand!(rng, limbs)
1,560,563✔
63
    @inbounds begin
1,560,563✔
64
        limbs[1] <<= sp.shift
1,560,563✔
65
        randbool = iszero(limbs[end] & Limb_high_bit)
1,560,563✔
66
        limbs[end] |= Limb_high_bit
1,560,563✔
67
    end
68
    z.sign = 1
1,560,563✔
69
    copyto!(z.d, limbs)
3,121,126✔
70
    randbool
1,560,563✔
71
end
72

73
function _rand!(rng::AbstractRNG, z::BigFloat, sp::SamplerBigFloat, ::CloseOpen12{BigFloat})
74
    _rand!(rng, z, sp)
3✔
75
    z.exp = 1
2✔
76
    z
2✔
77
end
78

79
function _rand!(rng::AbstractRNG, z::BigFloat, sp::SamplerBigFloat, ::CloseOpen01{BigFloat})
80
    randbool = _rand!(rng, z, sp)
1,560,562✔
81
    z.exp = 0
1,560,561✔
82
    randbool &&
1,560,561✔
83
        ccall((:mpfr_sub_d, :libmpfr), Int32,
84
              (Ref{BigFloat}, Ref{BigFloat}, Cdouble, Base.MPFR.MPFRRoundingMode),
85
              z, z, 0.5, Base.MPFR.ROUNDING_MODE[])
86
    z
1,560,561✔
87
end
88

89
# alternative, with 1 bit less of precision
90
# TODO: make an API for requesting full or not-full precision
91
function _rand!(rng::AbstractRNG, z::BigFloat, sp::SamplerBigFloat, ::CloseOpen01{BigFloat},
×
92
                ::Nothing)
93
    _rand!(rng, z, sp, CloseOpen12(BigFloat))
×
94
    ccall((:mpfr_sub_ui, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Culong, Base.MPFR.MPFRRoundingMode),
×
95
          z, z, 1, Base.MPFR.ROUNDING_MODE[])
96
    z
×
97
end
98

99
rand!(rng::AbstractRNG, z::BigFloat, sp::SamplerBigFloat{T}
×
100
      ) where {T<:FloatInterval{BigFloat}} =
1,560,565✔
101
          _rand!(rng, z, sp, T())
102

103
rand(rng::AbstractRNG, sp::SamplerBigFloat{T}) where {T<:FloatInterval{BigFloat}} =
1,560,561✔
104
    rand!(rng, BigFloat(; precision=sp.prec), sp)
105

106

107
### random integers
108

109
#### UniformBits
110

111
rand(r::AbstractRNG, ::SamplerTrivial{UInt10Raw{UInt16}}) = rand(r, UInt16)
6,703✔
112
rand(r::AbstractRNG, ::SamplerTrivial{UInt23Raw{UInt32}}) = rand(r, UInt32)
6,647✔
113

114
rand(r::AbstractRNG, ::SamplerTrivial{UInt52Raw{UInt64}}) =
5,656✔
115
    _rand52(r, rng_native_52(r))
116

117
_rand52(r::AbstractRNG, ::Type{Float64}) = reinterpret(UInt64, rand(r, CloseOpen12()))
×
118
_rand52(r::AbstractRNG, ::Type{UInt64})  = rand(r, UInt64)
5,656✔
119

120
rand(r::AbstractRNG, ::SamplerTrivial{UInt104Raw{UInt128}}) =
2✔
121
    rand(r, UInt52Raw(UInt128)) << 52 ⊻ rand(r, UInt52Raw(UInt128))
122

123
rand(r::AbstractRNG, ::SamplerTrivial{UInt10{UInt16}})   = rand(r, UInt10Raw())  & 0x03ff
6,703✔
124
rand(r::AbstractRNG, ::SamplerTrivial{UInt23{UInt32}})   = rand(r, UInt23Raw())  & 0x007fffff
6,647✔
125
rand(r::AbstractRNG, ::SamplerTrivial{UInt52{UInt64}})   = rand(r, UInt52Raw())  & 0x000fffffffffffff
6,727✔
126
rand(r::AbstractRNG, ::SamplerTrivial{UInt104{UInt128}}) = rand(r, UInt104Raw()) & 0x000000ffffffffffffffffffffffffff
×
127

128
rand(r::AbstractRNG, sp::SamplerTrivial{<:UniformBits{T}}) where {T} =
81,605✔
129
        rand(r, uint_default(sp[])) % T
130

131
#### BitInteger
132

133
# rand_generic methods are intended to help RNG implementers with common operations
134
# we don't call them simply `rand` as this can easily contribute to create
135
# ambiguities with user-side methods (forcing the user to resort to @eval)
136

137
rand_generic(r::AbstractRNG, T::Union{Bool,Int8,UInt8,Int16,UInt16,Int32,UInt32}) =
×
138
    rand(r, UInt52Raw()) % T[]
139

140
rand_generic(r::AbstractRNG, ::Type{UInt64}) =
×
141
    rand(r, UInt52Raw()) << 32 ⊻ rand(r, UInt52Raw())
142

143
rand_generic(r::AbstractRNG, ::Type{UInt128}) = _rand128(r, rng_native_52(r))
×
144

145
_rand128(r::AbstractRNG, ::Type{UInt64}) =
×
146
    ((rand(r, UInt64) % UInt128) << 64) ⊻ rand(r, UInt64)
147

148
function _rand128(r::AbstractRNG, ::Type{Float64})
×
149
    xor(rand(r, UInt52Raw(UInt128))  << 96,
×
150
        rand(r, UInt52Raw(UInt128))  << 48,
151
        rand(r, UInt52Raw(UInt128)))
152
end
153

154
rand_generic(r::AbstractRNG, ::Type{Int128}) = rand(r, UInt128) % Int128
×
155
rand_generic(r::AbstractRNG, ::Type{Int64})  = rand(r, UInt64) % Int64
×
156

157
### random complex numbers
158

159
rand(r::AbstractRNG, ::SamplerType{Complex{T}}) where {T<:Real} =
185,490✔
160
    complex(rand(r, T), rand(r, T))
161

162
### random characters
163

164
# returns a random valid Unicode scalar value (i.e. 0 - 0xd7ff, 0xe000 - # 0x10ffff)
165
function rand(r::AbstractRNG, ::SamplerType{T}) where {T<:AbstractChar}
6✔
166
    c = rand(r, 0x00000000:0x0010f7ff)
879✔
167
    (c < 0xd800) ? T(c) : T(c+0x800)
928✔
168
end
169

170
### random tuples
171

172
function Sampler(::Type{RNG}, ::Type{T}, n::Repetition) where {T<:Tuple, RNG<:AbstractRNG}
5✔
173
    tail_sp_ = Sampler(RNG, Tuple{Base.tail(fieldtypes(T))...}, n)
76✔
174
    SamplerTag{Ref{T}}((Sampler(RNG, fieldtype(T, 1), n), tail_sp_.data...))
71✔
175
    # Ref so that the gentype is `T` in SamplerTag's constructor
176
end
177

178
function Sampler(::Type{RNG}, ::Type{Tuple{Vararg{T, N}}}, n::Repetition) where {T, N, RNG<:AbstractRNG}
179
    if N > 0
72✔
180
        SamplerTag{Ref{Tuple{Vararg{T, N}}}}((Sampler(RNG, T, n),))
71✔
181
    else
182
        SamplerTag{Ref{Tuple{}}}(())
1✔
183
    end
184
end
185

186
function rand(rng::AbstractRNG, sp::SamplerTag{Ref{T}}) where T<:Tuple
193✔
187
    ntuple(i -> rand(rng, sp.data[min(i, length(sp.data))]), Val{fieldcount(T)}())::T
1,984✔
188
end
189

190
### random pairs
191

192
function Sampler(::Type{RNG}, ::Type{Pair{A, B}}, n::Repetition) where {RNG<:AbstractRNG, A, B}
193
    sp1 = Sampler(RNG, A, n)
32✔
194
    sp2 = A === B ? sp1 : Sampler(RNG, B, n)
32✔
195
    SamplerTag{Ref{Pair{A,B}}}(sp1 => sp2) # Ref so that the gentype is Pair{A, B}
32✔
196
                                           # in SamplerTag's constructor
197
end
198

199
rand(rng::AbstractRNG, sp::SamplerTag{<:Ref{<:Pair}}) =
296✔
200
    rand(rng, sp.data.first) => rand(rng, sp.data.second)
201

202

203
## Generate random integer within a range
204

205
### BitInteger
206

207
# there are three implemented samplers for unit ranges, the second one
208
# assumes that Float64 (i.e. 52 random bits) is the native type for the RNG:
209
# 1) "Fast" (SamplerRangeFast), which is most efficient when the range length is close
210
#    (or equal) to a power of 2 from below.
211
#    The tradeoff is faster creation of the sampler, but more consumption of entropy bits.
212
# 2) "Slow" (SamplerRangeInt) which tries to use as few entropy bits as possible, at the
213
#    cost of a bigger upfront price associated with the creation of the sampler.
214
#    This sampler is most appropriate for slower random generators.
215
# 3) "Nearly Division Less" (NDL) which is generally the fastest algorithm for types of size
216
#    up to 64 bits. This is the default for these types since Julia 1.5.
217
#    The "Fast" algorithm can be faster than NDL when the length of the range is
218
#    less than and close to a power of 2.
219

220
Sampler(::Type{<:AbstractRNG}, r::AbstractUnitRange{T},
×
221
        ::Repetition) where {T<:Base.BitInteger64} = SamplerRangeNDL(r)
5,055,064✔
222

223
Sampler(::Type{<:AbstractRNG}, r::AbstractUnitRange{T},
×
224
        ::Repetition) where {T<:Union{Int128,UInt128}} = SamplerRangeFast(r)
40✔
225

226
#### helper functions
227

228
uint_sup(::Type{<:Base.BitInteger32}) = UInt32
×
229
uint_sup(::Type{<:Union{Int64,UInt64}}) = UInt64
×
230
uint_sup(::Type{<:Union{Int128,UInt128}}) = UInt128
×
231

232
#### Fast
233

234
struct SamplerRangeFast{U<:BitUnsigned,T<:BitInteger} <: Sampler{T}
235
    a::T      # first element of the range
103✔
236
    bw::UInt  # bit width
237
    m::U      # range length - 1
238
    mask::U   # mask generated values before threshold rejection
239
end
240

241
SamplerRangeFast(r::AbstractUnitRange{T}) where T<:BitInteger =
103✔
242
    SamplerRangeFast(r, uint_sup(T))
243

244
function SamplerRangeFast(r::AbstractUnitRange{T}, ::Type{U}) where {T,U}
245
    isempty(r) && throw(ArgumentError("collection must be non-empty"))
103✔
246
    m = (last(r) - first(r)) % unsigned(T) % U # % unsigned(T) to not propagate sign bit
103✔
247
    bw = (Base.top_set_bit(m)) % UInt # bit-width
103✔
248
    mask = ((1 % U) << bw) - (1 % U)
103✔
249
    SamplerRangeFast{U,T}(first(r), bw, m, mask)
103✔
250
end
251

252
function rand(rng::AbstractRNG, sp::SamplerRangeFast{UInt32,T}) where T
36✔
253
    a, bw, m, mask = sp.a, sp.bw, sp.m, sp.mask
36✔
254
    # below, we don't use UInt32, to get reproducible values, whether Int is Int64 or Int32
255
    x = rand(rng, LessThan(m, Masked(mask, UInt52Raw(UInt32))))
38✔
256
    (x + a % UInt32) % T
36✔
257
end
258

259
has_fast_64(rng::AbstractRNG) = rng_native_52(rng) != Float64
73✔
260
# for MersenneTwister, both options have very similar performance
261

262
function rand(rng::AbstractRNG, sp::SamplerRangeFast{UInt64,T}) where T
9✔
263
    a, bw, m, mask = sp.a, sp.bw, sp.m, sp.mask
9✔
264
    if !has_fast_64(rng) && bw <= 52
9✔
265
        x = rand(rng, LessThan(m, Masked(mask, UInt52Raw())))
3✔
266
    else
267
        x = rand(rng, LessThan(m, Masked(mask, uniform(UInt64))))
7✔
268
    end
269
    (x + a % UInt64) % T
9✔
270
end
271

272
function rand(rng::AbstractRNG, sp::SamplerRangeFast{UInt128,T}) where T
34✔
273
    a, bw, m, mask = sp.a, sp.bw, sp.m, sp.mask
64✔
274
    if has_fast_64(rng)
64✔
275
        x = bw <= 64 ?
58✔
276
            rand(rng, LessThan(m % UInt64, Masked(mask % UInt64, uniform(UInt64)))) % UInt128 :
277
            rand(rng, LessThan(m, Masked(mask, uniform(UInt128))))
278
    else
279
        x = bw <= 52  ?
17✔
280
            rand(rng, LessThan(m % UInt64, Masked(mask % UInt64, UInt52Raw()))) % UInt128 :
281
        bw <= 104 ?
282
            rand(rng, LessThan(m, Masked(mask, UInt104Raw()))) :
283
            rand(rng, LessThan(m, Masked(mask, uniform(UInt128))))
284
    end
285
    x % T + a
64✔
286
end
287

288
#### "Slow" / SamplerRangeInt
289

290
# remainder function according to Knuth, where rem_knuth(a, 0) = a
291
rem_knuth(a::UInt, b::UInt) = a % (b + (b == 0)) + a * (b == 0)
9✔
292
rem_knuth(a::T, b::T) where {T<:Unsigned} = b != 0 ? a % b : a
54✔
293

294
# maximum multiple of k <= sup decremented by one,
295
# that is 0xFFFF...FFFF if k = (typemax(T) - typemin(T)) + 1 and sup == typemax(T) - 1
296
# with intentional underflow
297
# see https://stackoverflow.com/questions/29182036/integer-arithmetic-add-1-to-uint-max-and-divide-by-n-without-overflow
298

299
# sup == 0 means typemax(T) + 1
300
maxmultiple(k::T, sup::T=zero(T)) where {T<:Unsigned} =
1,670✔
301
    (div(sup - k, k + (k == 0))*k + k - one(k))::T
302

303
# similar but sup must not be equal to typemax(T)
304
unsafe_maxmultiple(k::T, sup::T) where {T<:Unsigned} =
118✔
305
    div(sup, k + (k == 0))*k - one(k)
306

307
struct SamplerRangeInt{T<:Integer,U<:Unsigned} <: Sampler{T}
308
    a::T      # first element of the range
185✔
309
    bw::Int   # bit width
310
    k::U      # range length or zero for full range
311
    u::U      # rejection threshold
312
end
313

314

315
SamplerRangeInt(r::AbstractUnitRange{T}) where T<:BitInteger =
207✔
316
    SamplerRangeInt(r, uint_sup(T))
317

318
function SamplerRangeInt(r::AbstractUnitRange{T}, ::Type{U}) where {T,U}
18✔
319
    isempty(r) && throw(ArgumentError("collection must be non-empty"))
185✔
320
    a = first(r)
185✔
321
    m = (last(r) - first(r)) % unsigned(T) % U
185✔
322
    k = m + one(U)
185✔
323
    bw = (Base.top_set_bit(m)) % Int
185✔
324
    mult = if U === UInt32
185✔
325
        maxmultiple(k)
36✔
326
    elseif U === UInt64
149✔
327
        bw <= 52 ? unsafe_maxmultiple(k, one(UInt64) << 52) :
156✔
328
                   maxmultiple(k)
329
    else # U === UInt128
330
        bw <= 52  ? unsafe_maxmultiple(k, one(UInt128) << 52) :
24✔
331
        bw <= 104 ? unsafe_maxmultiple(k, one(UInt128) << 104) :
332
                    maxmultiple(k)
333
    end
334

335
    SamplerRangeInt{T,U}(a, bw, k, mult) # overflow ok
185✔
336
end
337

338
rand(rng::AbstractRNG, sp::SamplerRangeInt{T,UInt32}) where {T<:BitInteger} =
36✔
339
    (unsigned(sp.a) + rem_knuth(rand(rng, LessThan(sp.u, UInt52Raw(UInt32))), sp.k)) % T
340

341
# this function uses 52 bit entropy for small ranges of length <= 2^52
342
function rand(rng::AbstractRNG, sp::SamplerRangeInt{T,UInt64}) where T<:BitInteger
9✔
343
    x = sp.bw <= 52 ? rand(rng, LessThan(sp.u, UInt52())) :
10✔
344
                      rand(rng, LessThan(sp.u, uniform(UInt64)))
345
    return ((sp.a % UInt64) + rem_knuth(x, sp.k)) % T
9✔
346
end
347

348
function rand(rng::AbstractRNG, sp::SamplerRangeInt{T,UInt128}) where T<:BitInteger
18✔
349
    x = sp.bw <= 52  ? rand(rng, LessThan(sp.u, UInt52(UInt128))) :
20✔
350
        sp.bw <= 104 ? rand(rng, LessThan(sp.u, UInt104(UInt128))) :
351
                       rand(rng, LessThan(sp.u, uniform(UInt128)))
352
    return ((sp.a % UInt128) + rem_knuth(x, sp.k)) % T
18✔
353
end
354

355
#### Nearly Division Less
356

357
# cf. https://arxiv.org/abs/1805.10941 (algorithm 5)
358

359
struct SamplerRangeNDL{U<:Unsigned,T} <: Sampler{T}
360
    a::T  # first element of the range
5,055,058✔
361
    s::U  # range length or zero for full range
362
end
363

364
function SamplerRangeNDL(r::AbstractUnitRange{T}) where {T}
45✔
365
    isempty(r) && throw(ArgumentError("collection must be non-empty"))
5,055,109✔
366
    a = first(r)
5,054,982✔
367
    U = uint_sup(T)
5,054,982✔
368
    s = (last(r) - first(r)) % unsigned(T) % U + one(U) # overflow ok
5,055,058✔
369
    # mod(-s, s) could be put in the Sampler object for repeated calls, but
370
    # this would be an advantage only for very big s and number of calls
371
    SamplerRangeNDL(a, s)
5,055,058✔
372
end
373

374
function rand(rng::AbstractRNG, sp::SamplerRangeNDL{U,T}) where {U,T}
8,688,758✔
375
    s = sp.s
8,688,758✔
376
    x = widen(rand(rng, U))
8,688,758✔
377
    m = x * s
8,688,758✔
378
    l = m % U
8,688,758✔
379
    if l < s
8,688,758✔
380
        t = mod(-s, s) # as s is unsigned, -s is equal to 2^L - s in the paper
87✔
381
        while l < t
103✔
382
            x = widen(rand(rng, U))
16✔
383
            m = x * s
16✔
384
            l = m % U
16✔
385
        end
16✔
386
    end
387
    (s == 0 ? x : m >> (8*sizeof(U))) % T + sp.a
8,688,758✔
388
end
389

390

391
### BigInt
392

393
struct SamplerBigInt{SP<:Sampler{Limb}} <: Sampler{BigInt}
394
    a::BigInt         # first
72✔
395
    m::BigInt         # range length - 1
396
    nlimbs::Int       # number of limbs in generated BigInt's (z ∈ [0, m])
397
    nlimbsmax::Int    # max number of limbs for z+a
398
    highsp::SP        # sampler for the highest limb of z
399
end
400

401
function SamplerBigInt(::Type{RNG}, r::AbstractUnitRange{BigInt}, N::Repetition=Val(Inf)
1✔
402
                       ) where {RNG<:AbstractRNG}
403
    m = last(r) - first(r)
73✔
404
    m.size < 0 && throw(ArgumentError("collection must be non-empty"))
72✔
405
    nlimbs = Int(m.size)
72✔
406
    hm = nlimbs == 0 ? Limb(0) : GC.@preserve m unsafe_load(m.d, nlimbs)
141✔
407
    highsp = Sampler(RNG, Limb(0):hm, N)
72✔
408
    nlimbsmax = max(nlimbs, abs(last(r).size), abs(first(r).size))
72✔
409
    return SamplerBigInt(first(r), m, nlimbs, nlimbsmax, highsp)
72✔
410
end
411

412
Sampler(::Type{RNG}, r::AbstractUnitRange{BigInt}, N::Repetition) where {RNG<:AbstractRNG} =
136✔
413
    SamplerBigInt(RNG, r, N)
414

415
rand(rng::AbstractRNG, sp::SamplerBigInt) =
8,071✔
416
    rand!(rng, BigInt(nbits = sp.nlimbsmax*8*sizeof(Limb)), sp)
417

418
function rand!(rng::AbstractRNG, x::BigInt, sp::SamplerBigInt)
8,078✔
419
    nlimbs = sp.nlimbs
8,078✔
420
    nlimbs == 0 && return MPZ.set!(x, sp.a)
8,078✔
421
    MPZ.realloc2!(x, sp.nlimbsmax*8*sizeof(Limb))
8,072✔
422
    @assert x.alloc >= nlimbs
8,072✔
423
    # we randomize x ∈ [0, m] with rejection sampling:
424
    # 1. the first nlimbs-1 limbs of x are uniformly randomized
425
    # 2. the high limb hx of x is sampled from 0:hm where hm is the
426
    #    high limb of m
427
    # We repeat 1. and 2. until x <= m
428
    hm = GC.@preserve sp unsafe_load(sp.m.d, nlimbs)
8,072✔
429
    GC.@preserve x begin
8,072✔
430
        limbs = UnsafeView(x.d, nlimbs-1)
8,072✔
431
        while true
8,072✔
432
            rand!(rng, limbs)
8,072✔
433
            hx = limbs[nlimbs] = rand(rng, sp.highsp)
8,072✔
434
            hx < hm && break # avoid calling mpn_cmp most of the time
8,072✔
435
            MPZ.mpn_cmp(x, sp.m, nlimbs) <= 0 && break
1✔
UNCOV
436
        end
×
437
        # adjust x.size (normally done by mpz_limbs_finish, in GMP version >= 6)
438
        while nlimbs > 0
8,074✔
439
            limbs[nlimbs] != 0 && break
8,072✔
440
            nlimbs -= 1
2✔
441
        end
2✔
442
        x.size = nlimbs
8,072✔
443
    end
444
    MPZ.add!(x, sp.a)
8,072✔
445
end
446

447

448
## random values from AbstractArray
449

450
Sampler(::Type{RNG}, r::AbstractArray, n::Repetition) where {RNG<:AbstractRNG} =
1,126,818✔
451
    SamplerSimple(r, Sampler(RNG, firstindex(r):lastindex(r), n))
452

453
rand(rng::AbstractRNG, sp::SamplerSimple{<:AbstractArray,<:Sampler}) =
1,217,290✔
454
    @inbounds return sp[][rand(rng, sp.data)]
455

456

457
## random values from Dict
458

459
function Sampler(::Type{RNG}, t::Dict, ::Repetition) where RNG<:AbstractRNG
460
    isempty(t) && throw(ArgumentError("collection must be non-empty"))
2,059✔
461
    # we use Val(Inf) below as rand is called repeatedly internally
462
    # even for generating only one random value from t
463
    SamplerSimple(t, Sampler(RNG, LinearIndices(t.slots), Val(Inf)))
2,043✔
464
end
465

466
function rand(rng::AbstractRNG, sp::SamplerSimple{<:Dict,<:Sampler})
2,146✔
467
    while true
13,517✔
468
        i = rand(rng, sp.data)
13,517✔
469
        Base.isslotfilled(sp[], i) && @inbounds return (sp[].keys[i] => sp[].vals[i])
13,517✔
470
    end
8,232✔
471
end
472

473
rand(rng::AbstractRNG, sp::SamplerTrivial{<:Base.KeySet{<:Any,<:Dict}}) =
1,035✔
474
    rand(rng, sp[].dict).first
475

476
rand(rng::AbstractRNG, sp::SamplerTrivial{<:Base.ValueIterator{<:Dict}}) =
684✔
477
    rand(rng, sp[].dict).second
478

479
## random values from Set
480

481
Sampler(::Type{RNG}, t::Set{T}, n::Repetition) where {RNG<:AbstractRNG,T} =
66✔
482
    SamplerTag{Set{T}}(Sampler(RNG, t.dict, n))
483

484
rand(rng::AbstractRNG, sp::SamplerTag{<:Set,<:Sampler}) = rand(rng, sp.data).first
1,313✔
485

486
## random values from BitSet
487

488
function Sampler(RNG::Type{<:AbstractRNG}, t::BitSet, n::Repetition)
62✔
489
    isempty(t) && throw(ArgumentError("collection must be non-empty"))
62✔
490
    SamplerSimple(t, Sampler(RNG, minimum(t):maximum(t), Val(Inf)))
54✔
491
end
492

493
function rand(rng::AbstractRNG, sp::SamplerSimple{BitSet,<:Sampler})
6✔
494
    while true
3,335✔
495
        n = rand(rng, sp.data)
3,335✔
496
        n in sp[] && return n
3,335✔
497
    end
2,649✔
498
end
499

500
## random values from AbstractDict/AbstractSet
501

502
# we defer to _Sampler to avoid ambiguities with a call like Sampler(rng, Set(1), Val(1))
503
Sampler(RNG::Type{<:AbstractRNG}, t::Union{AbstractDict,AbstractSet}, n::Repetition) =
1,015✔
504
    _Sampler(RNG, t, n)
505

506
# avoid linear complexity for repeated calls
507
_Sampler(RNG::Type{<:AbstractRNG}, t::Union{AbstractDict,AbstractSet}, n::Val{Inf}) =
180✔
508
    Sampler(RNG, collect(t), n)
509

510
# when generating only one element, avoid the call to collect
511
_Sampler(::Type{<:AbstractRNG}, t::Union{AbstractDict,AbstractSet}, ::Val{1}) =
835✔
512
    SamplerTrivial(t)
513

514
function nth(iter, n::Integer)::eltype(iter)
464✔
515
    for (i, x) in enumerate(iter)
928✔
516
        i == n && return x
3,738✔
517
    end
3,274✔
518
end
519

520
rand(rng::AbstractRNG, sp::SamplerTrivial{<:Union{AbstractDict,AbstractSet}}) =
472✔
521
    nth(sp[], rand(rng, 1:length(sp[])))
522

523

524
## random characters from a string
525

526
# we use collect(str), which is most of the time more efficient than specialized methods
527
# (except maybe for very small arrays)
528
Sampler(RNG::Type{<:AbstractRNG}, str::AbstractString, n::Val{Inf}) = Sampler(RNG, collect(str), n)
2,344✔
529

530
# when generating only one char from a string, the specialized method below
531
# is usually more efficient
532
Sampler(RNG::Type{<:AbstractRNG}, str::AbstractString, ::Val{1}) =
24✔
533
    SamplerSimple(str, Sampler(RNG, 1:_lastindex(str), Val(Inf)))
534

535
isvalid_unsafe(s::String, i) = !Base.is_valid_continuation(GC.@preserve s unsafe_load(pointer(s), i))
319✔
536
isvalid_unsafe(s::AbstractString, i) = isvalid(s, i)
265✔
537
_lastindex(s::String) = sizeof(s)
12✔
538
_lastindex(s::AbstractString) = lastindex(s)
12✔
539

540
function rand(rng::AbstractRNG, sp::SamplerSimple{<:AbstractString,<:Sampler})::Char
464✔
541
    str = sp[]
464✔
542
    while true
584✔
543
        pos = rand(rng, sp.data)
584✔
544
        isvalid_unsafe(str, pos) && return str[pos]
584✔
545
    end
120✔
546
end
547

548

549
## random elements from tuples
550

551
### 1
552

553
Sampler(::Type{<:AbstractRNG}, t::Tuple{A}, ::Repetition) where {A} =
2✔
554
    SamplerTrivial(t)
555

556
rand(rng::AbstractRNG, sp::SamplerTrivial{Tuple{A}}) where {A} =
2✔
557
    @inbounds return sp[][1]
558

559
### 2
560

561
Sampler(RNG::Type{<:AbstractRNG}, t::Tuple{A,B}, n::Repetition) where {A,B} =
17✔
562
    SamplerSimple(t, Sampler(RNG, Bool, n))
563

564
rand(rng::AbstractRNG, sp::SamplerSimple{Tuple{A,B}}) where {A,B} =
302✔
565
    @inbounds return sp[][1 + rand(rng, sp.data)]
566

567
### 3
568

569
Sampler(RNG::Type{<:AbstractRNG}, t::Tuple{A,B,C}, n::Repetition) where {A,B,C} =
2✔
570
    SamplerSimple(t, Sampler(RNG, UInt52(), n))
571

572
function rand(rng::AbstractRNG, sp::SamplerSimple{Tuple{A,B,C}}) where {A,B,C}
2✔
573
    local r
2✔
574
    while true
2✔
575
        r = rand(rng, sp.data)
2✔
576
        r != 0x000fffffffffffff && break # _very_ likely
2✔
577
    end
×
578
    @inbounds return sp[][1 + r ÷ 0x0005555555555555]
2✔
579
end
580

581
### n
582

583
@generated function Sampler(RNG::Type{<:AbstractRNG}, t::Tuple, n::Repetition)
4,012✔
584
    l = fieldcount(t)
14✔
585
    if l < typemax(UInt32) && ispow2(l)
14✔
586
        :(SamplerSimple(t, Sampler(RNG, UInt32, n)))
8✔
587
    else
588
        :(SamplerSimple(t, Sampler(RNG, Base.OneTo(length(t)), n)))
14✔
589
    end
590
end
591

592
@generated function rand(rng::AbstractRNG, sp::SamplerSimple{T}) where T<:Tuple
2,010✔
593
    l = fieldcount(T)
11✔
594
    if l < typemax(UInt32) && ispow2(l)
11✔
595
        quote
5✔
596
            r = rand(rng, sp.data) & ($l-1)
2,002✔
597
            @inbounds return sp[][1 + r]
2,002✔
598
        end
599
    else
600
        :(@inbounds return sp[][rand(rng, sp.data)])
11✔
601
    end
602
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc