ronantakizawa/codereview-bench huggingface dataset:
Repository: JuliaLang/julia
File: base/iterators.jl (Julia)

Code before:
    if IteratorSize(I) isa Union{HasShape, HasLength}
        _nth(itr.xs, mod1(n, length(itr.xs)))
    else
        _nth(itr, n)
    end
end

# finite cycle: in reality a Flatten{Take{Repeated{O}}} iterator
function nth(itr::Flatten{Take{Repeated{O}}}, n::Integer) where {O}
    if IteratorSize(O) isa Union{HasShape, HasLength}
        cycles = itr.it.n
        repeated = itr.it.xs.x
        k = length(repeated)
        n > k*cycles ? throw(BoundsError(itr, n)) : _nth(repeated, mod1(n, k))
    else
        _nth(itr, n)
    end
end

@inline _nth(itr, n) = first(drop(itr, n-1))
@inline _nth(itr::AbstractArray, n) = itr[begin + n-1]
"""
    nth(n::Integer)

Return a function that gets the `n`-th element from any iterator passed to it.
Throw a `BoundsError`[@ref] if not existing.

Fixes the second element. Equivalent to `Base.Fix2(nth, n)`.
Will advance any `Stateful`[@ref] iterator.

See also: [`nth`](@ref), [`Base.Fix2`](@ref)
# Examples
```jldoctest
julia> fifth_element = Iterators.nth(5)
(::Base.Fix2{typeof(nth), Int64}) (generic function with 1 method)

julia> fifth_element(reshape(1:30, (5,6)))
5

julia> map(Iterators.nth(3), my_vec)
```
"""
nth(n::Integer) = Base.Fix2(nth, n)

end


Review comment (suggestion):
I would delete this `nth(n)` function.   We don't have that for `getindex` or any similar function, so I don't see why we should have it hear.  People can always just write `itr -> nth(itr, n)` if they need this.

Code after:
end

# Flatten{Take{Repeated{O}}} is the actual type of an Iterators.cycle(iterable::O, m) iterator
function _nth(::Union{HasShape, HasLength}, itr::Flatten{Take{Repeated{O}}}, n::Integer) where {O}
    cycles = itr.it.n
    torepeat = itr.it.xs.x
    k = length(torepeat)
    (n > k*cycles || k == 0) && throw(BoundsError(itr, n))

    # prevent wrap around behaviour and inherit the error handling
    return nth(torepeat, n > 0 ? mod1(n, k) : n)
end

function _nth(::IteratorSize, itr, n::Integer)
    # unrolled version of `first(drop)`
    n > 0 || throw(BoundsError(itr, n))
    y = iterate(itr)
    for _ in 1:n-1
        y === nothing && break
        y = iterate(itr, y[2])
    end
    y === nothing && throw(BoundsError(itr, n))
    y[1]
end
"""
    nth(n::Integer)

Return a function that gets the `n`-th element from any iterator passed to it.
Equivalent to `Base.Fix2(nth, n)` or `itr -> nth(itr, n)`.

See also: [`nth`](@ref), [`Base.Fix2`](@ref)
# Examples
```jldoctest
julia> fifth_element = Iterators.nth(5)
(::Base.Fix2{typeof(Base.Iterators.nth), Int64}) (generic function with 2 methods)

julia> fifth_element(reshape(1:30, (5,6)))
5

julia> map(fifth_element, ("Willis", "Jovovich", "Oldman"))
('i', 'v', 'a')
```
"""
nth(n::Integer) = Base.Fix2(nth, n)

end
Repository: JuliaLang/julia
File: base/iterators.jl (Julia)

Code before:
# Take(Repeated)
nth(itr::Take{<:Repeated}, n::Integer) =
    n > itr.n ? throw(BoundsError(itr, n)) : nth(itr.xs, n)

# infinite cycle
function nth(itr::Cycle{I}, n::Integer) where {I}
    if IteratorSize(I) isa Union{HasShape, HasLength}
        _nth(itr.xs, mod1(n, length(itr.xs)))
    else
        _nth(itr, n)
    end
end

# finite cycle: in reality a Flatten{Take{Repeated{O}}} iterator
function nth(itr::Flatten{Take{Repeated{O}}}, n::Integer) where {O}
    if IteratorSize(O) isa Union{HasShape, HasLength}
        cycles = itr.it.n
        repeated = itr.it.xs.x
        k = length(repeated)
        n > k*cycles ? throw(BoundsError(itr, n)) : _nth(repeated, mod1(n, k))
    else
        _nth(itr, n)
    end
end

@inline _nth(itr, n) = first(drop(itr, n-1))
@inline _nth(itr::AbstractArray, n) = itr[begin + n-1]
"""
    nth(n::Integer)

Return a function that gets the `n`-th element from any iterator passed to it.
Equivalent to `Base.Fix2(nth, n)` or `itr -> nth(itr, n)`.

See also: [`nth`](@ref), [`Base.Fix2`](@ref)
# Examples
```jldoctest
julia> fifth_element = Iterators.nth(5)
(::Base.Fix2{typeof(Base.Iterators.nth), Int64}) (generic function with 2 methods)

julia> fifth_element(reshape(1:30, (5,6)))
5

julia> map(fifth_element, ("Willis", "Jovovich", "Oldman"))
('i', 'v', 'a')
```
"""
nth(n::Integer) = Base.Fix2(nth, n)

end


Review comment (suggestion):
did any benchmarking show these `@inline` are needed? not that these calls shouldn't be inlined, but I'm just curious if there was a situation where the compiler didn't do that already

Code after:

function _nth(::Union{HasShape, HasLength}, itr::Cycle{I}, n::Integer) where {I}
    N = length(itr.xs)
    N == 0 && throw(BoundsError(itr, n))

    # prevents wrap around behaviour and inherit the error handling
    return nth(itr.xs, n > 0 ? mod1(n, N) : n)
end

# Flatten{Take{Repeated{O}}} is the actual type of an Iterators.cycle(iterable::O, m) iterator
function _nth(::Union{HasShape, HasLength}, itr::Flatten{Take{Repeated{O}}}, n::Integer) where {O}
    cycles = itr.it.n
    torepeat = itr.it.xs.x
    k = length(torepeat)
    (n > k*cycles || k == 0) && throw(BoundsError(itr, n))

    # prevent wrap around behaviour and inherit the error handling
    return nth(torepeat, n > 0 ? mod1(n, k) : n)
end

function _nth(::IteratorSize, itr, n::Integer)
    # unrolled version of `first(drop)`
    n > 0 || throw(BoundsError(itr, n))
    y = iterate(itr)
    for _ in 1:n-1
        y === nothing && break
        y = iterate(itr, y[2])
    end
    y === nothing && throw(BoundsError(itr, n))
    y[1]
end
"""
    nth(n::Integer)

Return a function that gets the `n`-th element from any iterator passed to it.
Equivalent to `Base.Fix2(nth, n)` or `itr -> nth(itr, n)`.

See also: [`nth`](@ref), [`Base.Fix2`](@ref)
# Examples
```jldoctest
julia> fifth_element = Iterators.nth(5)
(::Base.Fix2{typeof(Base.Iterators.nth), Int64}) (generic function with 2 methods)

julia> fifth_element(reshape(1:30, (5,6)))
5

julia> map(fifth_element, ("Willis", "Jovovich", "Oldman"))
('i', 'v', 'a')
```
"""
nth(n::Integer) = Base.Fix2(nth, n)
Repository: JuliaLang/julia
File: base/abstractarray.jl (Julia)

Code before:

## 2 argument
function map!(f::F, dest::AbstractArray, A::AbstractArray, B::AbstractArray) where F
    for (i, j, k) in zip(eachindex(dest), eachindex(A), eachindex(B))
        @inbounds a, b = A[j], B[k]
        val = f(a, b)
        @inbounds dest[i] = val
    end
    return dest
end

## N argument

@inline ith_all(i, ::Tuple{}) = ()
function ith_all(i, as)
    @_propagate_inbounds_meta
    return (as[1][i], ith_all(i, tail(as))...)
end

function map_n!(f::F, dest::AbstractArray, As) where F
    idxs1 = eachindex(LinearIndices(As[1]))
    @boundscheck begin
        idxs1 = intersect(map(eachindex ∘ LinearIndices, As)...)
        checkbounds(dest, idxs1)
    end
    for i = idxs1
        @inbounds I = ith_all(i, As)
        val = f(I...)
        @inbounds dest[i] = val
    end
    return dest
end

"""
    map!(function, destination, collection...)

Like [`map`](@ref), but stores the result in `destination` rather than a new
collection. `destination` must be at least as large as the smallest collection.

$(_DOCS_ALIASING_WARNING)

See also: [`map`](@ref), [`foreach`](@ref), [`zip`](@ref), [`copyto!`](@ref).

# Examples
```jldoctest
julia> a = zeros(3);

julia> map!(x -> x * 2, a, [1, 2, 3]);

julia> a
3-element Vector{Float64}:

Review comment (performance):
```suggestion
    idxs = mapreduce(LinearIndices, intersect, As)
    checkbounds(dest, inds)
    for i = idxs
```

- I don't see why we need `eachindex` is here
- We should only include bounds-check elision if benchmarks reveal that it makes a difference. In this case, we would also need to add `@propagate_inbounds` to `map!` to make it take effect.
- I think that `mapreduce(LinearIndices, intersect, As)` is more elegant than `intersect(map(LinearIndices, As)...)`, unless there is some performance reason to use the latter.
- If always compute the intersection of indices then `inds` is a better name than `inds1`

Code after:
See also: [`map`](@ref), [`foreach`](@ref), [`zip`](@ref), [`copyto!`](@ref).

# Examples
```jldoctest
julia> a = zeros(3);

julia> map!(x -> x * 2, a, [1, 2, 3]);

julia> a
3-element Vector{Float64}:
 2.0
 4.0
 6.0

julia> map!(+, zeros(Int, 5), 100:999, 1:3)
5-element Vector{$(Int)}:
 101
 103
 105
   0
   0
```
"""
function map!(f::F, dest::AbstractArray, As::AbstractArray...) where {F}
    @assert !isempty(As) # should dispatch to map!(f, A)
    map_n!(f, dest, As)
end

"""
    map!(function, array)

Like [`map`](@ref), but stores the result in the same array.
!!! compat "Julia 1.12"
    This method requires Julia 1.12 or later. To support previous versions too,
    use the equivalent `map!(function, array, array)`.

# Examples
```jldoctest
julia> a = [1 2 3; 4 5 6];

julia> map!(x -> x^3, a);

julia> a
2×3 Matrix{$Int}:
  1    8   27
 64  125  216
```
"""
map!(f::F, inout::AbstractArray) where F = map!(f, inout, inout)

"""
Repository: JuliaLang/julia
File: base/abstractarray.jl (Julia)

Code before:
```jldoctest
julia> map(x -> x * 2, [1, 2, 3])
3-element Vector{Int64}:
 2
 4
 6

julia> map(+, [1, 2, 3], [10, 20, 30, 400, 5000])
3-element Vector{Int64}:
 11
 22
 33
```
"""
map(f, A) = collect(Generator(f,A)) # default to returning an Array for `map` on general iterators

map(f, ::AbstractDict) = error("map is not defined on dictionaries")
map(f, ::AbstractSet) = error("map is not defined on sets")

## 2 argument
function map!(f::F, dest::AbstractArray, A::AbstractArray, B::AbstractArray) where F
    IL = IndexLinear()
    inds = intersect(eachindex(IL, A), eachindex(IL, B))
    @boundscheck checkbounds(dest, inds)
    for i = inds
        dest[i] = f(A[i], B[i])
    end
    return dest
end

## N argument

@inline ith_all(i, ::Tuple{}) = ()
function ith_all(i, as)
    @_propagate_inbounds_meta
    return (as[1][i], ith_all(i, tail(as))...)
end

function map_n!(f::F, dest::AbstractArray, As) where F
    inds = mapreduce(Fix1(eachindex, IndexLinear()), intersect, As)
    @boundscheck checkbounds(dest, inds)
    for i = inds
        dest[i] = f(ith_all(i, As)...)
    end
    return dest
end

"""
    map!(function, destination, collection...)

Like [`map`](@ref), but stores the result in `destination` rather than a new

Review comment (suggestion):
Similarly:

```suggestion
    length(dest) < min(length(A), length(B)) && throw(ArgumentError("destination is smaller than the smallest mapped argument"))
    for (i, j, k) in zip(eachindex(dest), eachindex(A), eachindex(B))
        @inbounds a, b = A[j], B[k]
        val = f(a, b)
        @inbounds dest[i] = val
        dest[i] = f(A[i], B[i])
```

Code after:
```jldoctest
julia> map(x -> x * 2, [1, 2, 3])
3-element Vector{Int64}:
 2
 4
 6

julia> map(+, [1, 2, 3], [10, 20, 30, 400, 5000])
3-element Vector{Int64}:
 11
 22
 33
```
"""
map(f, A) = collect(Generator(f,A)) # default to returning an Array for `map` on general iterators

map(f, ::AbstractDict) = error("map is not defined on dictionaries")
map(f, ::AbstractSet) = error("map is not defined on sets")

## 2 argument
function map!(f::F, dest::AbstractArray, A::AbstractArray, B::AbstractArray) where F
    for (i, j, k) in zip(eachindex(dest), eachindex(A), eachindex(B))
        @inbounds a, b = A[j], B[k]
        val = f(a, b)
        @inbounds dest[i] = val
    end
    return dest
end

## N argument

@inline ith_all(i, ::Tuple{}) = ()
function ith_all(i, as)
    @_propagate_inbounds_meta
    return (as[1][i], ith_all(i, tail(as))...)
end

function map_n!(f::F, dest::AbstractArray, As) where F
    idxs = LinearIndices(dest)
    if all(x -> LinearIndices(x) == idxs, As)
        for i in idxs
            @inbounds as = ith_all(i, As)
            val = f(as...)
            @inbounds dest[i] = val
        end
    else
        for (i, Is...) in zip(eachindex(dest), map(eachindex, As)...)
            as = ntuple(j->getindex(As[j], Is[j]), length(As))
            val = f(as...)
            dest[i] = val
        end
Repository: JuliaLang/julia
File: stdlib/Random/src/misc.jl (Julia)

Code before:

function shuffle(rng::AbstractRNG, tup::(Tuple{Vararg{T, N}} where {T})) where {N}
    @inline let  # `@inline` and `@inbounds` are here to help escape analysis
        # use a narrow integer type to save stack space and prevent heap allocation
        Ind = if N ≤ typemax(UInt8)
            UInt8
        elseif N ≤ typemax(UInt16)
            UInt16
        else
            UInt
        end
        clo = @inbounds let mem = Memory{Ind}(undef, N)
            randperm!(rng, mem)
            let mem = mem, tup = tup
                function closure(i::Int)
                    @inbounds tup[mem[i]]
                end
            end
        end
        ntuple(clo, Val{N}())::typeof(tup)
    end
end

function shuffle(tup::NTuple)
    shuffle(default_rng(), tup)
end

"""
    shuffle!([rng=default_rng(),] v::AbstractArray)

In-place version of [`shuffle`](@ref): randomly permute `v` in-place,
optionally supplying the random-number generator `rng`.

# Examples
```jldoctest
julia> shuffle!(Xoshiro(123), Vector(1:10))
10-element Vector{Int64}:
  5
  4
  2
  3
  6
 10
  8
  1
  9
  7
```
"""
function shuffle!(r::AbstractRNG, a::AbstractArray)
    # keep it consistent with `randperm!` and `randcycle!` if possible

Review comment (suggestion):
Personal preference, feel free to ignore:
```suggestion
shuffle(tup::NTuple) = shuffle(default_rng(), tup)
```

Code after:

function shuffle(rng::AbstractRNG, tup::NTuple{N}) where {N}
    # `@inline` and `@inbounds` are here to help escape analysis eliminate the `Memory` allocation
    #
    # * `@inline` might be necessary because escape analysis relies on everything
    #   touching the `Memory` being inlined because there's no interprocedural escape
    #   analysis yet, relevant WIP PR: https://github.com/JuliaLang/julia/pull/56849
    #
    # * `@inbounds` might be necessary because escape analysis requires any throws of
    #   `BoundsError` to be eliminated as dead code, because `BoundsError` stores the
    #   array itself, making the throw escape the array from the function, relevant
    #   WIP PR: https://github.com/JuliaLang/julia/pull/56167
    @inline let
        # use a narrow integer type to save stack space and prevent heap allocation
        Ind = if N ≤ typemax(UInt8)
            UInt8
        elseif N ≤ typemax(UInt16)
            UInt16
        else
            UInt
        end
        mem = @inbounds randperm!(rng, Memory{Ind}(undef, N))
        function closure(i::Int)
            @inbounds tup[mem[i]]
        end
        ntuple(closure, Val{N}())
    end
end

"""
    shuffle!([rng=default_rng(),] v::AbstractArray)

In-place version of [`shuffle`](@ref): randomly permute `v` in-place,
optionally supplying the random-number generator `rng`.

# Examples
```jldoctest
julia> shuffle!(Xoshiro(123), Vector(1:10))
10-element Vector{Int64}:
  5
  4
  2
  3
  6
 10
  8
  1
  9
  7
```
"""
Repository: JuliaLang/julia
File: stdlib/Random/src/misc.jl (Julia)

Code before:

Return a randomly permuted copy of `v`. The optional `rng` argument specifies a random
number generator (see [Random Numbers](@ref)).
To permute `v` in-place, see [`shuffle!`](@ref). To obtain randomly permuted
indices, see [`randperm`](@ref).

# Examples
```jldoctest
julia> shuffle(Xoshiro(123), Vector(1:10))
10-element Vector{Int64}:
  5
  4
  2
  3
  6
 10
  8
  1
  9
  7
```
"""
function shuffle end

shuffle(r::AbstractRNG, a::AbstractArray) = shuffle!(r, copymutable(a))
shuffle(a::AbstractArray) = shuffle(default_rng(), a)

shuffle(r::AbstractRNG, a::Base.OneTo) = randperm(r, last(a))

## randperm & randperm!

"""
    randperm([rng=default_rng(),] n::Integer)

Construct a random permutation of length `n`. The optional `rng`
argument specifies a random number generator (see [Random
Numbers](@ref)). The element type of the result is the same as the type
of `n`.

To randomly permute an arbitrary vector, see [`shuffle`](@ref) or
[`shuffle!`](@ref).

!!! compat "Julia 1.1"
    In Julia 1.1 `randperm` returns a vector `v` with `eltype(v) == typeof(n)`
    while in Julia 1.0 `eltype(v) == Int`.

# Examples
```jldoctest
julia> randperm(Xoshiro(123), 4)
4-element Vector{Int64}:
 1

Review comment (refactor):
```suggestion
shuffle(a::Union{Tuple, AbstractArray}) = shuffle(default_rng(), a)
```

Code after:
"""
    shuffle([rng=default_rng(),] v::Union{NTuple,AbstractArray})

Return a randomly permuted copy of `v`. The optional `rng` argument specifies a random
number generator (see [Random Numbers](@ref)).
To permute `v` in-place, see [`shuffle!`](@ref). To obtain randomly permuted
indices, see [`randperm`](@ref).

!!! compat "Julia 1.13"
    Shuffling an `NTuple` value requires Julia v1.13 or above.

# Examples
```jldoctest
julia> shuffle(Xoshiro(123), Vector(1:10))
10-element Vector{Int64}:
  5
  4
  2
  3
  6
 10
  8
  1
  9
  7
```
"""
function shuffle end

shuffle(r::AbstractRNG, a::AbstractArray) = shuffle!(r, copymutable(a))
shuffle(a::Union{NTuple, AbstractArray}) = shuffle(default_rng(), a)

shuffle(r::AbstractRNG, a::Base.OneTo) = randperm(r, last(a))

## randperm & randperm!

"""
    randperm([rng=default_rng(),] n::Integer)

Construct a random permutation of length `n`. The optional `rng`
argument specifies a random number generator (see [Random
Numbers](@ref)). The element type of the result is the same as the type
of `n`.

To randomly permute an arbitrary vector, see [`shuffle`](@ref) or
[`shuffle!`](@ref).

!!! compat "Julia 1.1"
    In Julia 1.1 `randperm` returns a vector `v` with `eltype(v) == typeof(n)`
    while in Julia 1.0 `eltype(v) == Int`.
Repository: JuliaLang/julia
File: stdlib/Random/test/runtests.jl (Julia)

Code before:
    @test maximum(m) <= 0.106
end

@testset "`shuffle(::NTuple)`" begin
    @testset "sorted" begin
        for n ∈ 0:20
            tup = ntuple(identity, n)
            @test tup === sort(@inferred shuffle(tup))
        end
    end
    @testset "not identity" begin
        function is_not_identity_at_least_once()
            function f(::Any)
                tup = ntuple(identity, 9)
                tup !== shuffle(tup)
            end
            @test any(f, 1:1000000)
        end
        is_not_identity_at_least_once()
    end
    @testset "no heap allocation" begin
        function no_heap_allocation(tup)
            @test iszero(@allocated shuffle(tup))
        end
        for n ∈ 0:9
            tup = ntuple(identity, n)
            no_heap_allocation(tup)
        end
    end
end

# issue #42752
# test that running finalizers that launch tasks doesn't change RNG stream
function f42752(do_gc::Bool, cell = (()->Any[[]])())
    a = rand()
    if do_gc
        finalizer(cell[1]) do _
            @async nothing
        end
        cell[1] = nothing
        GC.gc()
    end
    b = rand()
    (a, b)
end
guardseed() do
    for _ in 1:4
        Random.seed!(1)
        val = f42752(false)
        Random.seed!(1)
        @test f42752(true) === val

Review comment (bug):
This testset fails with `--check-bounds=yes` (which is how the test suite gets run), so I'm just going to delete it. The reason is, as discussed, that escape analysis fails in the presence of bounds checking.

```suggestion
```

Code after:
    # Test that shuffle! is uniformly random on BitArrays
    rng = MersenneTwister(123)
    a = (reshape(1:(4*5), 4, 5) .<= 2) # 4x5 BitMatrix whose first two elements are true, rest are false
    m = sum(1:50_000) do _
        shuffle!(rng, a)
    end/50_000 # mean result of shuffle!-ing a 50_000 times. If the shuffle! is uniform, then each index has a
    # 10% chance of having a true in it, so each value should converge to 0.1.
    @test minimum(m) >= 0.094
    @test maximum(m) <= 0.106
end

@testset "`shuffle(::NTuple)`" begin
    @testset "sorted" begin
        for n ∈ 0:20
            tup = ntuple(identity, n)
            @test tup === sort(@inferred shuffle(tup))
        end
    end
    @testset "not identity" begin
        function shuffle_is_identity()
            tup = ntuple(identity, 9)
            tup === shuffle(tup)
        end
        # shuffling may behave as the identity sometimes, but if it doesn't manage to actually reorder some of the elements at least once, something is wrong
        @test any((_ -> !shuffle_is_identity()), 1:1000000)
    end
end

# issue #42752
# test that running finalizers that launch tasks doesn't change RNG stream
function f42752(do_gc::Bool, cell = (()->Any[[]])())
    a = rand()
    if do_gc
        finalizer(cell[1]) do _
            @async nothing
        end
        cell[1] = nothing
        GC.gc()
    end
    b = rand()
    (a, b)
end
guardseed() do
    for _ in 1:4
        Random.seed!(1)
        val = f42752(false)
        Random.seed!(1)
        @test f42752(true) === val
    end
end
Repository: JuliaLang/julia
File: stdlib/Random/src/misc.jl (Julia)

Code before:
        j = 1 + rand(r, ltm52(i, mask))
        a[i], a[j] = a[j], a[i]
        i == 1 + mask && (mask = 2 * mask + 1)
    end
    return a
end

function shuffle!(r::AbstractRNG, a::AbstractArray{Bool})
    old_count = count(a)
    len = length(a)
    uncommon_value = 2old_count <= len
    fuel = uncommon_value ? old_count : len - old_count
    fuel == 0 && return a
    a .= !uncommon_value
    while fuel > 0
        k = rand(r, eachindex(a))
        fuel -= a[k] != uncommon_value
        a[k] = uncommon_value
    end
    a
end

shuffle!(a::AbstractArray) = shuffle!(default_rng(), a)

"""
    shuffle([rng=default_rng(),] v::Union{Tuple,AbstractArray})

Return a randomly permuted copy of `v`. The optional `rng` argument specifies a random
number generator (see [Random Numbers](@ref)).
To permute `v` in-place, see [`shuffle!`](@ref). To obtain randomly permuted
indices, see [`randperm`](@ref).

# Examples
```jldoctest
julia> shuffle(Xoshiro(123), Vector(1:10))
10-element Vector{Int64}:
  5
  4
  2
  3
  6
 10
  8
  1
  9
  7
```
"""
function shuffle end

shuffle(r::AbstractRNG, a::AbstractArray) = shuffle!(r, copymutable(a))

Review comment (refactor):
```suggestion
    shuffle([rng=default_rng(),] v::Union{NTuple,AbstractArray})
```

Code after:
    require_one_based_indexing(a)
    n = length(a)
    @assert n <= Int64(2)^52
    n == 0 && return a
    mask = 3
    @inbounds for i = 2:n
        j = 1 + rand(r, ltm52(i, mask))
        a[i], a[j] = a[j], a[i]
        i == 1 + mask && (mask = 2 * mask + 1)
    end
    return a
end

function shuffle!(r::AbstractRNG, a::AbstractArray{Bool})
    old_count = count(a)
    len = length(a)
    uncommon_value = 2old_count <= len
    fuel = uncommon_value ? old_count : len - old_count
    fuel == 0 && return a
    a .= !uncommon_value
    while fuel > 0
        k = rand(r, eachindex(a))
        fuel -= a[k] != uncommon_value
        a[k] = uncommon_value
    end
    a
end

shuffle!(a::AbstractArray) = shuffle!(default_rng(), a)

"""
    shuffle([rng=default_rng(),] v::Union{NTuple,AbstractArray})

Return a randomly permuted copy of `v`. The optional `rng` argument specifies a random
number generator (see [Random Numbers](@ref)).
To permute `v` in-place, see [`shuffle!`](@ref). To obtain randomly permuted
indices, see [`randperm`](@ref).

!!! compat "Julia 1.13"
    Shuffling an `NTuple` value requires Julia v1.13 or above.

# Examples
```jldoctest
julia> shuffle(Xoshiro(123), Vector(1:10))
10-element Vector{Int64}:
  5
  4
  2
  3
  6
 10