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

JuliaLang / julia / #37527

pending completion
#37527

push

local

web-flow
make `IRShow.method_name` inferrable (#49607)

18 of 18 new or added lines in 3 files covered. (100.0%)

68710 of 81829 relevant lines covered (83.97%)

33068903.12 hits per line

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

80.55
/base/multidimensional.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
### Multidimensional iterators
4
module IteratorsMD
5
    import .Base: eltype, length, size, first, last, in, getindex, setindex!, IndexStyle,
6
                  min, max, zero, oneunit, isless, eachindex, ndims, IteratorSize,
7
                  convert, show, iterate, promote_rule
8

9
    import .Base: +, -, *, (:)
10
    import .Base: simd_outer_range, simd_inner_length, simd_index, setindex
11
    import .Base: to_indices, to_index, _to_indices1, _cutdim
12
    using .Base: IndexLinear, IndexCartesian, AbstractCartesianIndex, fill_to_length, tail,
13
        ReshapedArray, ReshapedArrayLF, OneTo, Fix1
14
    using .Base.Iterators: Reverse, PartitionIterator
15
    using .Base: @propagate_inbounds
16

17
    export CartesianIndex, CartesianIndices
18

19
    """
20
        CartesianIndex(i, j, k...)   -> I
21
        CartesianIndex((i, j, k...)) -> I
22

23
    Create a multidimensional index `I`, which can be used for
24
    indexing a multidimensional array `A`.  In particular, `A[I]` is
25
    equivalent to `A[i,j,k...]`.  One can freely mix integer and
26
    `CartesianIndex` indices; for example, `A[Ipre, i, Ipost]` (where
27
    `Ipre` and `Ipost` are `CartesianIndex` indices and `i` is an
28
    `Int`) can be a useful expression when writing algorithms that
29
    work along a single dimension of an array of arbitrary
30
    dimensionality.
31

32
    A `CartesianIndex` is sometimes produced by [`eachindex`](@ref), and
33
    always when iterating with an explicit [`CartesianIndices`](@ref).
34

35
    An `I::CartesianIndex` is treated as a "scalar" (not a container)
36
    for `broadcast`.   In order to iterate over the components of a
37
    `CartesianIndex`, convert it to a tuple with `Tuple(I)`.
38

39
    # Examples
40
    ```jldoctest
41
    julia> A = reshape(Vector(1:16), (2, 2, 2, 2))
42
    2×2×2×2 Array{Int64, 4}:
43
    [:, :, 1, 1] =
44
     1  3
45
     2  4
46

47
    [:, :, 2, 1] =
48
     5  7
49
     6  8
50

51
    [:, :, 1, 2] =
52
      9  11
53
     10  12
54

55
    [:, :, 2, 2] =
56
     13  15
57
     14  16
58

59
    julia> A[CartesianIndex((1, 1, 1, 1))]
60
    1
61

62
    julia> A[CartesianIndex((1, 1, 1, 2))]
63
    9
64

65
    julia> A[CartesianIndex((1, 1, 2, 1))]
66
    5
67
    ```
68

69
    !!! compat "Julia 1.10"
70
        Using a `CartesianIndex` as a "scalar" for `broadcast` requires
71
        Julia 1.10; in previous releases, use `Ref(I)`.
72
    """
73
    struct CartesianIndex{N} <: AbstractCartesianIndex{N}
74
        I::NTuple{N,Int}
75
        CartesianIndex{N}(index::NTuple{N,Integer}) where {N} = new(index)
304,095,138✔
76
    end
77

78
    CartesianIndex(index::NTuple{N,Integer}) where {N} = CartesianIndex{N}(index)
304,564,181✔
79
    CartesianIndex(index::Integer...) = CartesianIndex(index)
191,801,510✔
80
    CartesianIndex{N}(index::Vararg{Integer,N}) where {N} = CartesianIndex{N}(index)
177✔
81
    # Allow passing tuples smaller than N
82
    CartesianIndex{N}(index::Tuple) where {N} = CartesianIndex{N}(fill_to_length(index, 1, Val(N)))
5✔
83
    CartesianIndex{N}(index::Integer...) where {N} = CartesianIndex{N}(index)
2✔
84
    CartesianIndex{N}() where {N} = CartesianIndex{N}(())
1✔
85
    # Un-nest passed CartesianIndexes
86
    CartesianIndex(index::Union{Integer, CartesianIndex}...) = CartesianIndex(flatten(index))
37,495,303✔
87
    flatten(::Tuple{}) = ()
×
88
    flatten(I::Tuple{Any}) = Tuple(I[1])
37,495,303✔
89
    @inline flatten(I::Tuple) = (Tuple(I[1])..., flatten(tail(I))...)
37,495,308✔
90
    CartesianIndex(index::Tuple{Vararg{Union{Integer, CartesianIndex}}}) = CartesianIndex(index...)
14✔
91
    show(io::IO, i::CartesianIndex) = (print(io, "CartesianIndex"); show(io, i.I))
16✔
92

93
    # length
94
    length(::CartesianIndex{N}) where {N} = N
205,844,512✔
95
    length(::Type{CartesianIndex{N}}) where {N} = N
×
96

97
    # indexing
98
    getindex(index::CartesianIndex, i::Integer) = index.I[i]
42,691✔
99
    Base.get(A::AbstractArray, I::CartesianIndex, default) = get(A, I.I, default)
4✔
100
    eltype(::Type{T}) where {T<:CartesianIndex} = eltype(fieldtype(T, :I))
×
101

102
    # access to index tuple
103
    Tuple(index::CartesianIndex) = index.I
37,628,693✔
104

105
    Base.setindex(x::CartesianIndex,i,j) = CartesianIndex(Base.setindex(Tuple(x),i,j))
1✔
106

107
    # equality
108
    Base.:(==)(a::CartesianIndex{N}, b::CartesianIndex{N}) where N = a.I == b.I
2,492,657✔
109

110
    # zeros and ones
111
    zero(::CartesianIndex{N}) where {N} = zero(CartesianIndex{N})
1✔
112
    zero(::Type{CartesianIndex{N}}) where {N} = CartesianIndex(ntuple(Returns(0), Val(N)))
346✔
113
    oneunit(::CartesianIndex{N}) where {N} = oneunit(CartesianIndex{N})
3✔
114
    oneunit(::Type{CartesianIndex{N}}) where {N} = CartesianIndex(ntuple(Returns(1), Val(N)))
4✔
115

116
    # arithmetic, min/max
117
    @inline (-)(index::CartesianIndex{N}) where {N} =
1✔
118
        CartesianIndex{N}(map(-, index.I))
119
    @inline (+)(index1::CartesianIndex{N}, index2::CartesianIndex{N}) where {N} =
22✔
120
        CartesianIndex{N}(map(+, index1.I, index2.I))
121
    @inline (-)(index1::CartesianIndex{N}, index2::CartesianIndex{N}) where {N} =
505✔
122
        CartesianIndex{N}(map(-, index1.I, index2.I))
123
    @inline min(index1::CartesianIndex{N}, index2::CartesianIndex{N}) where {N} =
1✔
124
        CartesianIndex{N}(map(min, index1.I, index2.I))
125
    @inline max(index1::CartesianIndex{N}, index2::CartesianIndex{N}) where {N} =
1✔
126
        CartesianIndex{N}(map(max, index1.I, index2.I))
127

128
    @inline (*)(a::Integer, index::CartesianIndex{N}) where {N} = CartesianIndex{N}(map(x->a*x, index.I))
16✔
129
    @inline (*)(index::CartesianIndex, a::Integer) = *(a,index)
1✔
130

131
    # comparison
132
    isless(I1::CartesianIndex{N}, I2::CartesianIndex{N}) where {N} = isless(reverse(I1.I), reverse(I2.I))
28,398✔
133

134
    # conversions
135
    convert(::Type{T}, index::CartesianIndex{1}) where {T<:Number} = convert(T, index[1])
2✔
136
    convert(::Type{T}, index::CartesianIndex) where {T<:Tuple} = convert(T, index.I)
1✔
137

138
    # hashing
139
    const cartindexhash_seed = UInt == UInt64 ? 0xd60ca92f8284b8b0 : 0xf2ea7c2e
140
    function Base.hash(ci::CartesianIndex, h::UInt)
28,314✔
141
        h += cartindexhash_seed
28,314✔
142
        for i in ci.I
28,314✔
143
            h = hash(i, h)
56,626✔
144
        end
84,939✔
145
        return h
28,314✔
146
    end
147

148
    # nextind and prevind with CartesianIndex
149
    function Base.nextind(a::AbstractArray{<:Any,N}, i::CartesianIndex{N}) where {N}
100✔
150
        iter = CartesianIndices(axes(a))
100✔
151
        # might overflow
152
        I = inc(i.I, iter.indices)
120✔
153
        return I
100✔
154
    end
155
    function Base.prevind(a::AbstractArray{<:Any,N}, i::CartesianIndex{N}) where {N}
104✔
156
        iter = CartesianIndices(axes(a))
104✔
157
        # might underflow
158
        I = dec(i.I, iter.indices)
132✔
159
        return I
104✔
160
    end
161

162
    Base._ind2sub(t::Tuple, ind::CartesianIndex) = Tuple(ind)
110✔
163

164
    # Iteration over the elements of CartesianIndex cannot be supported until its length can be inferred,
165
    # see #23719
166
    Base.iterate(::CartesianIndex) =
1✔
167
        error("iteration is deliberately unsupported for CartesianIndex. Use `I` rather than `I...`, or use `Tuple(I)...`")
168

169
    # Iteration
170
    const OrdinalRangeInt = OrdinalRange{Int, Int}
171
    """
172
        CartesianIndices(sz::Dims) -> R
173
        CartesianIndices((istart:[istep:]istop, jstart:[jstep:]jstop, ...)) -> R
174

175
    Define a region `R` spanning a multidimensional rectangular range
176
    of integer indices. These are most commonly encountered in the
177
    context of iteration, where `for I in R ... end` will return
178
    [`CartesianIndex`](@ref) indices `I` equivalent to the nested loops
179

180
        for j = jstart:jstep:jstop
181
            for i = istart:istep:istop
182
                ...
183
            end
184
        end
185

186
    Consequently these can be useful for writing algorithms that
187
    work in arbitrary dimensions.
188

189
        CartesianIndices(A::AbstractArray) -> R
190

191
    As a convenience, constructing a `CartesianIndices` from an array makes a
192
    range of its indices.
193

194
    !!! compat "Julia 1.6"
195
        The step range method `CartesianIndices((istart:istep:istop, jstart:[jstep:]jstop, ...))`
196
        requires at least Julia 1.6.
197

198
    # Examples
199
    ```jldoctest
200
    julia> foreach(println, CartesianIndices((2, 2, 2)))
201
    CartesianIndex(1, 1, 1)
202
    CartesianIndex(2, 1, 1)
203
    CartesianIndex(1, 2, 1)
204
    CartesianIndex(2, 2, 1)
205
    CartesianIndex(1, 1, 2)
206
    CartesianIndex(2, 1, 2)
207
    CartesianIndex(1, 2, 2)
208
    CartesianIndex(2, 2, 2)
209

210
    julia> CartesianIndices(fill(1, (2,3)))
211
    CartesianIndices((2, 3))
212
    ```
213

214
    ## Conversion between linear and cartesian indices
215

216
    Linear index to cartesian index conversion exploits the fact that a
217
    `CartesianIndices` is an `AbstractArray` and can be indexed linearly:
218

219
    ```jldoctest
220
    julia> cartesian = CartesianIndices((1:3, 1:2))
221
    CartesianIndices((1:3, 1:2))
222

223
    julia> cartesian[4]
224
    CartesianIndex(1, 2)
225

226
    julia> cartesian = CartesianIndices((1:2:5, 1:2))
227
    CartesianIndices((1:2:5, 1:2))
228

229
    julia> cartesian[2, 2]
230
    CartesianIndex(3, 2)
231
    ```
232

233
    ## Broadcasting
234

235
    `CartesianIndices` support broadcasting arithmetic (+ and -) with a `CartesianIndex`.
236

237
    !!! compat "Julia 1.1"
238
        Broadcasting of CartesianIndices requires at least Julia 1.1.
239

240
    ```jldoctest
241
    julia> CIs = CartesianIndices((2:3, 5:6))
242
    CartesianIndices((2:3, 5:6))
243

244
    julia> CI = CartesianIndex(3, 4)
245
    CartesianIndex(3, 4)
246

247
    julia> CIs .+ CI
248
    CartesianIndices((5:6, 9:10))
249
    ```
250

251
    For cartesian to linear index conversion, see [`LinearIndices`](@ref).
252
    """
253
    struct CartesianIndices{N,R<:NTuple{N,OrdinalRangeInt}} <: AbstractArray{CartesianIndex{N},N}
254
        indices::R
512,501✔
255
    end
256

257
    CartesianIndices(::Tuple{}) = CartesianIndices{0,typeof(())}(())
3,800✔
258
    function CartesianIndices(inds::NTuple{N,OrdinalRange{<:Integer, <:Integer}}) where {N}
4,447✔
259
        indices = map(r->convert(OrdinalRangeInt, r), inds)
17,058✔
260
        CartesianIndices{N, typeof(indices)}(indices)
4,447✔
261
    end
262

263
    CartesianIndices(index::CartesianIndex) = CartesianIndices(index.I)
1✔
264
    CartesianIndices(inds::NTuple{N,Union{<:Integer,OrdinalRange{<:Integer}}}) where {N} =
1,610✔
265
        CartesianIndices(map(_convert2ind, inds))
266

267
    CartesianIndices(A::AbstractArray) = CartesianIndices(axes(A))
108,381✔
268

269
    _convert2ind(sz::Bool) = Base.OneTo(Int8(sz))
6✔
270
    _convert2ind(sz::Integer) = Base.OneTo(sz)
3,810✔
271
    _convert2ind(sz::AbstractUnitRange) = first(sz):last(sz)
29✔
272
    _convert2ind(sz::OrdinalRange) = first(sz):step(sz):last(sz)
18✔
273

274
    function show(io::IO, iter::CartesianIndices)
4✔
275
        print(io, "CartesianIndices(")
4✔
276
        show(io, map(_xform_index, iter.indices))
4✔
277
        print(io, ")")
4✔
278
    end
279
    _xform_index(i) = i
1✔
280
    _xform_index(i::OneTo) = i.stop
2✔
281
    show(io::IO, ::MIME"text/plain", iter::CartesianIndices) = show(io, iter)
×
282

283
    """
284
        (:)(start::CartesianIndex, [step::CartesianIndex], stop::CartesianIndex)
285

286
    Construct [`CartesianIndices`](@ref) from two `CartesianIndex` and an optional step.
287

288
    !!! compat "Julia 1.1"
289
        This method requires at least Julia 1.1.
290

291
    !!! compat "Julia 1.6"
292
        The step range method start:step:stop requires at least Julia 1.6.
293

294
    # Examples
295
    ```jldoctest
296
    julia> I = CartesianIndex(2,1);
297

298
    julia> J = CartesianIndex(3,3);
299

300
    julia> I:J
301
    CartesianIndices((2:3, 1:3))
302

303
    julia> I:CartesianIndex(1, 2):J
304
    CartesianIndices((2:1:3, 1:2:3))
305
    ```
306
    """
307
    (:)(I::CartesianIndex{N}, J::CartesianIndex{N}) where N =
9✔
308
        CartesianIndices(map((i,j) -> i:j, Tuple(I), Tuple(J)))
18✔
309
    (:)(I::CartesianIndex{N}, S::CartesianIndex{N}, J::CartesianIndex{N}) where N =
12✔
310
        CartesianIndices(map((i,s,j) -> i:s:j, Tuple(I), Tuple(S), Tuple(J)))
24✔
311

312
    promote_rule(::Type{CartesianIndices{N,R1}}, ::Type{CartesianIndices{N,R2}}) where {N,R1,R2} =
2✔
313
        CartesianIndices{N,Base.indices_promote_type(R1,R2)}
314

315
    convert(::Type{Tuple{}}, R::CartesianIndices{0}) = ()
×
316
    for RT in (OrdinalRange{Int, Int}, StepRange{Int, Int}, AbstractUnitRange{Int})
317
        @eval convert(::Type{NTuple{N,$RT}}, R::CartesianIndices{N}) where {N} =
2✔
318
            map(x->convert($RT, x), R.indices)
4✔
319
    end
320
    convert(::Type{NTuple{N,AbstractUnitRange}}, R::CartesianIndices{N}) where {N} =
2✔
321
        convert(NTuple{N,AbstractUnitRange{Int}}, R)
322
    convert(::Type{NTuple{N,UnitRange{Int}}}, R::CartesianIndices{N}) where {N} =
1✔
323
        UnitRange{Int}.(convert(NTuple{N,AbstractUnitRange}, R))
324
    convert(::Type{NTuple{N,UnitRange}}, R::CartesianIndices{N}) where {N} =
1✔
325
        UnitRange.(convert(NTuple{N,AbstractUnitRange}, R))
326
    convert(::Type{Tuple{Vararg{AbstractUnitRange{Int}}}}, R::CartesianIndices{N}) where {N} =
×
327
        convert(NTuple{N,AbstractUnitRange{Int}}, R)
328
    convert(::Type{Tuple{Vararg{AbstractUnitRange}}}, R::CartesianIndices) =
×
329
        convert(Tuple{Vararg{AbstractUnitRange{Int}}}, R)
330
    convert(::Type{Tuple{Vararg{UnitRange{Int}}}}, R::CartesianIndices{N}) where {N} =
1✔
331
        convert(NTuple{N,UnitRange{Int}}, R)
332
    convert(::Type{Tuple{Vararg{UnitRange}}}, R::CartesianIndices) =
1✔
333
        convert(Tuple{Vararg{UnitRange{Int}}}, R)
334

335
    convert(::Type{CartesianIndices{N,R}}, inds::CartesianIndices{N}) where {N,R} =
2✔
336
        CartesianIndices(convert(R, inds.indices))::CartesianIndices{N,R}
337

338
    # equality
339
    Base.:(==)(a::CartesianIndices{N}, b::CartesianIndices{N}) where N =
2,152✔
340
        all(map(==, a.indices, b.indices))
341
    Base.:(==)(a::CartesianIndices, b::CartesianIndices) = false
×
342

343
    # AbstractArray implementation
344
    Base.axes(iter::CartesianIndices{N,R}) where {N,R} = map(Base.axes1, iter.indices)
1,351,447✔
345
    Base.IndexStyle(::Type{CartesianIndices{N,R}}) where {N,R} = IndexCartesian()
2,835✔
346
    Base.has_offset_axes(iter::CartesianIndices) = Base.has_offset_axes(iter.indices...)
1✔
347
    # getindex for a 0D CartesianIndices is necessary for disambiguation
348
    @propagate_inbounds function Base.getindex(iter::CartesianIndices{0,R}) where {R}
3✔
349
        CartesianIndex()
3✔
350
    end
351
    @inline function Base.getindex(iter::CartesianIndices{N,R}, I::Vararg{Int, N}) where {N,R}
607,907✔
352
        # Eagerly do boundscheck before calculating each item of the CartesianIndex so that
353
        # we can pass `@inbounds` hint to inside the map and generates more efficient SIMD codes (#42115)
354
        @boundscheck checkbounds(iter, I...)
607,907✔
355
        index = map(iter.indices, I) do r, i
607,907✔
356
            @inbounds getindex(r, i)
1,641,978✔
357
        end
358
        CartesianIndex(index)
607,907✔
359
    end
360

361
    # CartesianIndices act as a multidimensional range, so cartesian indexing of CartesianIndices
362
    # with compatible dimensions may be seen as indexing into the component ranges.
363
    # This may use the special indexing behavior implemented for ranges to return another CartesianIndices
364
    @inline function Base.getindex(iter::CartesianIndices{N,R},
2,054✔
365
        I::Vararg{Union{OrdinalRange{<:Integer, <:Integer}, Colon}, N}) where {N,R}
366
        @boundscheck checkbounds(iter, I...)
2,054✔
367
        indices = map(iter.indices, I) do r, i
2,054✔
368
            @inbounds getindex(r, i)
2,119✔
369
        end
370
        CartesianIndices(indices)
2,054✔
371
    end
372
    @propagate_inbounds function Base.getindex(iter::CartesianIndices{N},
17✔
373
        C::CartesianIndices{N}) where {N}
374
        getindex(iter, C.indices...)
17✔
375
    end
376
    @inline Base.getindex(iter::CartesianIndices{0}, ::CartesianIndices{0}) = iter
1✔
377

378
    # If dimensions permit, we may index into a CartesianIndices directly instead of constructing a SubArray wrapper
379
    @propagate_inbounds function Base.view(c::CartesianIndices{N}, r::Vararg{Union{OrdinalRange{<:Integer, <:Integer}, Colon},N}) where {N}
2,006✔
380
        getindex(c, r...)
2,006✔
381
    end
382
    @propagate_inbounds function Base.view(c::CartesianIndices{N}, C::CartesianIndices{N}) where {N}
1✔
383
        getindex(c, C)
1✔
384
    end
385

386
    ndims(R::CartesianIndices) = ndims(typeof(R))
953✔
387
    ndims(::Type{CartesianIndices{N}}) where {N} = N
×
388
    ndims(::Type{CartesianIndices{N,TT}}) where {N,TT} = N
953✔
389

390
    eachindex(::IndexCartesian, A::AbstractArray) = CartesianIndices(axes(A))
98,627✔
391

392
    @inline function eachindex(::IndexCartesian, A::AbstractArray, B::AbstractArray...)
3✔
393
        axsA = axes(A)
3✔
394
        Base._all_match_first(axes, axsA, B...) || Base.throw_eachindex_mismatch_indices(IndexCartesian(), axes(A), axes.(B)...)
4✔
395
        CartesianIndices(axsA)
2✔
396
    end
397

398
    eltype(::Type{CartesianIndices{N}}) where {N} = CartesianIndex{N}
1✔
399
    eltype(::Type{CartesianIndices{N,TT}}) where {N,TT} = CartesianIndex{N}
5,548✔
400
    IteratorSize(::Type{<:CartesianIndices{N}}) where {N} = Base.HasShape{N}()
167✔
401

402
    @inline function iterate(iter::CartesianIndices)
260,874✔
403
        iterfirst = first(iter)
114,709✔
404
        if !all(map(in, iterfirst.I, iter.indices))
263,189✔
405
            return nothing
202✔
406
        end
407
        iterfirst, iterfirst
261,093✔
408
    end
409
    @inline function iterate(iter::CartesianIndices, state)
96,212,908✔
410
        valid, I = __inc(state.I, iter.indices)
97,508,069✔
411
        valid || return nothing
96,468,343✔
412
        return CartesianIndex(I...), CartesianIndex(I...)
95,961,971✔
413
    end
414

415
    # increment & carry
416
    @inline function inc(state, indices)
1,184,220✔
417
        _, I = __inc(state, indices)
1,245,472✔
418
        return CartesianIndex(I...)
1,184,220✔
419
    end
420

421
    # Unlike ordinary ranges, CartesianIndices continues the iteration in the next column when the
422
    # current column is consumed. The implementation is written recursively to achieve this.
423
    # `iterate` returns `Union{Nothing, Tuple}`, we explicitly pass a `valid` flag to eliminate
424
    # the type instability inside the core `__inc` logic, and this gives better runtime performance.
425
    __inc(::Tuple{}, ::Tuple{}) = false, ()
×
426
    @inline function __inc(state::Tuple{Int}, indices::Tuple{OrdinalRangeInt})
853,432✔
427
        rng = indices[1]
82,340✔
428
        I = state[1] + step(rng)
1,768,070✔
429
        valid = __is_valid_range(I, rng) && state[1] != last(rng)
1,768,077✔
430
        return valid, (I, )
1,768,070✔
431
    end
432
    @inline function __inc(state::Tuple{Int,Int,Vararg{Int}}, indices::Tuple{OrdinalRangeInt,OrdinalRangeInt,Vararg{OrdinalRangeInt}})
97,084,875✔
433
        rng = indices[1]
97,084,875✔
434
        I = state[1] + step(rng)
97,084,875✔
435
        if __is_valid_range(I, rng) && state[1] != last(rng)
97,084,910✔
436
            return true, (I, tail(state)...)
95,631,307✔
437
        end
438
        valid, I = __inc(tail(state), tail(indices))
1,552,970✔
439
        return valid, (first(rng), I...)
1,453,568✔
440
    end
441

442
    @inline __is_valid_range(I, rng::AbstractUnitRange) = I in rng
98,808,380✔
443
    @inline function __is_valid_range(I, rng::OrdinalRange)
169,282✔
444
        if step(rng) > 0
169,282✔
445
            lo, hi = first(rng), last(rng)
169,070✔
446
        else
447
            lo, hi = last(rng), first(rng)
212✔
448
        end
449
        lo <= I <= hi
170,503✔
450
    end
451

452
    # 0-d cartesian ranges are special-cased to iterate once and only once
453
    iterate(iter::CartesianIndices{0}, done=false) = done ? nothing : (CartesianIndex(), true)
11,401✔
454

455
    size(iter::CartesianIndices) = map(length, iter.indices)
128,534✔
456

457
    length(iter::CartesianIndices) = prod(size(iter))
127,063✔
458

459
    # make CartesianIndices a multidimensional range
460
    Base.step(iter::CartesianIndices) = CartesianIndex(map(step, iter.indices))
9✔
461

462
    first(iter::CartesianIndices) = CartesianIndex(map(first, iter.indices))
250,807✔
463
    last(iter::CartesianIndices)  = CartesianIndex(map(last, iter.indices))
9,846✔
464

465
    # When used as indices themselves, CartesianIndices can simply become its tuple of ranges
466
    _to_indices1(A, inds, I1::CartesianIndices) = map(Fix1(to_index, A), I1.indices)
336✔
467
    _cutdim(inds::Tuple, I1::CartesianIndices) = split(inds, Val(ndims(I1)))[2]
362✔
468

469
    # but preserve CartesianIndices{0} as they consume a dimension.
470
    _to_indices1(A, inds, I1::CartesianIndices{0}) = (I1,)
26✔
471

472
    @inline in(i::CartesianIndex, r::CartesianIndices) = false
×
473
    @inline in(i::CartesianIndex{N}, r::CartesianIndices{N}) where {N} = all(map(in, i.I, r.indices))
43✔
474

475
    simd_outer_range(iter::CartesianIndices{0}) = iter
×
476
    function simd_outer_range(iter::CartesianIndices)
148,840✔
477
        CartesianIndices(tail(iter.indices))
148,840✔
478
    end
479

480
    simd_inner_length(iter::CartesianIndices{0}, ::CartesianIndex) = 1
×
481
    simd_inner_length(iter::CartesianIndices, I::CartesianIndex) = Base.length(iter.indices[1])
935,941✔
482

483
    simd_index(iter::CartesianIndices{0}, ::CartesianIndex, I1::Int) = first(iter)
×
484
    @propagate_inbounds simd_index(iter::CartesianIndices, Ilast::CartesianIndex, I1::Int) =
37,495,289✔
485
        CartesianIndex(iter.indices[1][I1+firstindex(iter.indices[1])], Ilast)
486

487
    # Split out the first N elements of a tuple
488
    @inline function split(t, V::Val)
205,846,896✔
489
        ref = ntuple(Returns(true), V)  # create a reference tuple of length N
205,846,888✔
490
        _split1(t, ref), _splitrest(t, ref)
205,846,899✔
491
    end
492
    @inline _split1(t, ref) = (t[1], _split1(tail(t), tail(ref))...)
4,152✔
493
    @inline _splitrest(t, ref) = _splitrest(tail(t), tail(ref))
4,163✔
494
    # exit either when we've exhausted the input or reference tuple
495
    _split1(::Tuple{}, ::Tuple{}) = ()
×
496
    _split1(::Tuple{}, ref) = ()
205,844,431✔
497
    _split1(t, ::Tuple{}) = ()
1,701✔
498
    _splitrest(::Tuple{}, ::Tuple{}) = ()
×
499
    _splitrest(t, ::Tuple{}) = t
1,701✔
500
    _splitrest(::Tuple{}, ref) = ()
205,844,431✔
501

502
    @inline function split(I::CartesianIndex, V::Val)
×
503
        i, j = split(I.I, V)
×
504
        CartesianIndex(i), CartesianIndex(j)
×
505
    end
506
    function split(R::CartesianIndices, V::Val)
×
507
        i, j = split(R.indices, V)
×
508
        CartesianIndices(i), CartesianIndices(j)
×
509
    end
510

511
    # reversed CartesianIndices iteration
512
    @inline function Base._reverse(iter::CartesianIndices, ::Colon)
4✔
513
        CartesianIndices(reverse.(iter.indices))
4✔
514
    end
515

516
    Base.@constprop :aggressive function Base._reverse(iter::CartesianIndices, dim::Integer)
5✔
517
        1 <= dim <= ndims(iter) || throw(ArgumentError(Base.LazyString("invalid dimension ", dim, " in reverse")))
7✔
518
        ndims(iter) == 1 && return Base._reverse(iter, :)
3✔
519
        indices = iter.indices
3✔
520
        return CartesianIndices(Base.setindex(indices, reverse(indices[dim]), dim))
3✔
521
    end
522

523
    Base.@constprop :aggressive function Base._reverse(iter::CartesianIndices, dims::Tuple{Vararg{Integer}})
7✔
524
        indices = iter.indices
7✔
525
        # use `sum` to force const fold
526
        dimrev = ntuple(i -> sum(==(i), dims; init = 0) == 1, Val(length(indices)))
28✔
527
        length(dims) == sum(dimrev) || throw(ArgumentError(Base.LazyString("invalid dimensions ", dims, " in reverse")))
10✔
528
        length(dims) == length(indices) && return Base._reverse(iter, :)
4✔
529
        indices′ = map((i, f) -> f ? reverse(i) : i, indices, dimrev)
18✔
530
        return CartesianIndices(indices′)
4✔
531
    end
532

533
    # fix ambiguity with array.jl:
534
    Base._reverse(iter::CartesianIndices{1}, dims::Tuple{Integer}) =
×
535
        Base._reverse(iter, first(dims))
536

537
    @inline function iterate(r::Reverse{<:CartesianIndices})
14✔
538
        iterfirst = last(r.itr)
14✔
539
        if !all(map(in, iterfirst.I, r.itr.indices))
16✔
540
            return nothing
×
541
        end
542
        iterfirst, iterfirst
14✔
543
    end
544
    @inline function iterate(r::Reverse{<:CartesianIndices}, state)
260✔
545
        valid, I = __dec(state.I, r.itr.indices)
322✔
546
        valid || return nothing
278✔
547
        return CartesianIndex(I...), CartesianIndex(I...)
242✔
548
    end
549

550
    # decrement & carry
551
    @inline function dec(state, indices)
118,256✔
552
        _, I = __dec(state, indices)
124,313✔
553
        return CartesianIndex(I...)
118,256✔
554
    end
555

556
    # decrement post check to avoid integer overflow
557
    @inline __dec(::Tuple{}, ::Tuple{}) = false, ()
×
558
    @inline function __dec(state::Tuple{Int}, indices::Tuple{OrdinalRangeInt})
6,117✔
559
        rng = indices[1]
6,117✔
560
        I = state[1] - step(rng)
6,117✔
561
        valid = __is_valid_range(I, rng) && state[1] != first(rng)
6,274✔
562
        return valid, (I,)
6,117✔
563
    end
564
    @inline function __dec(state::Tuple{Int,Int,Vararg{Int}}, indices::Tuple{OrdinalRangeInt,OrdinalRangeInt,Vararg{OrdinalRangeInt}})
118,590✔
565
        rng = indices[1]
118,590✔
566
        I = state[1] - step(rng)
118,590✔
567
        if __is_valid_range(I, rng) && state[1] != first(rng)
119,612✔
568
            return true, (I, tail(state)...)
112,399✔
569
        end
570
        valid, I = __dec(tail(state), tail(indices))
6,420✔
571
        return valid, (last(rng), I...)
6,191✔
572
    end
573

574
    # 0-d cartesian ranges are special-cased to iterate once and only once
575
    iterate(iter::Reverse{<:CartesianIndices{0}}, state=false) = state ? nothing : (CartesianIndex(), true)
×
576

577
    function Base.LinearIndices(inds::CartesianIndices{N,R}) where {N,R<:NTuple{N, AbstractUnitRange}}
1,519✔
578
        LinearIndices{N,R}(inds.indices)
1,519✔
579
    end
580
    function Base.LinearIndices(inds::CartesianIndices)
6✔
581
        indices = inds.indices
6✔
582
        if all(x->step(x)==1, indices)
17✔
583
            indices = map(rng->first(rng):last(rng), indices)
3✔
584
            LinearIndices{length(indices), typeof(indices)}(indices)
1✔
585
        else
586
            # Given the fact that StepRange 1:2:4 === 1:2:3, we lost the original size information
587
            # and thus cannot calculate the correct linear indices when the steps are not 1.
588
            throw(ArgumentError("LinearIndices for $(typeof(inds)) with non-1 step size is not yet supported."))
5✔
589
        end
590
    end
591

592
    # This is currently needed because converting to LinearIndices is only available when steps are
593
    # all 1
594
    # NOTE: this is only a temporary patch and could be possibly removed when StepRange support to
595
    # LinearIndices is done
596
    function Base.collect(inds::CartesianIndices{N, R}) where {N,R<:NTuple{N, AbstractUnitRange}}
1,517✔
597
        Base._collect_indices(axes(inds), inds)
1,517✔
598
    end
599
    function Base.collect(inds::CartesianIndices)
514✔
600
        dest = Array{eltype(inds), ndims(inds)}(undef, size(inds))
514✔
601
        i = 0
514✔
602
        @inbounds for a in inds
1,028✔
603
            dest[i+=1] = a
3,401✔
604
        end
3,915✔
605
        dest
514✔
606
    end
607

608
    # array operations
609
    Base.intersect(a::CartesianIndices{N}, b::CartesianIndices{N}) where N =
2✔
610
        CartesianIndices(intersect.(a.indices, b.indices))
611

612
    # Views of reshaped CartesianIndices are used for partitions — ensure these are fast
613
    const CartesianPartition{T<:CartesianIndex, P<:CartesianIndices, R<:ReshapedArray{T,1,P}} = SubArray{T,1,R,<:Tuple{AbstractUnitRange{Int}},false}
614
    eltype(::Type{PartitionIterator{T}}) where {T<:ReshapedArrayLF} = SubArray{eltype(T), 1, T, Tuple{UnitRange{Int}}, true}
×
615
    eltype(::Type{PartitionIterator{T}}) where {T<:ReshapedArray} = SubArray{eltype(T), 1, T, Tuple{UnitRange{Int}}, false}
4✔
616
    Iterators.IteratorEltype(::Type{<:PartitionIterator{T}}) where {T<:ReshapedArray} = Iterators.IteratorEltype(T)
1✔
617

618
    eltype(::Type{PartitionIterator{T}}) where {T<:OneTo} = UnitRange{eltype(T)}
4✔
619
    eltype(::Type{PartitionIterator{T}}) where {T<:Union{UnitRange, StepRange, StepRangeLen, LinRange}} = T
26✔
620
    Iterators.IteratorEltype(::Type{<:PartitionIterator{T}}) where {T<:Union{OneTo, UnitRange, StepRange, StepRangeLen, LinRange}} = Iterators.IteratorEltype(T)
15✔
621

622
    @inline function iterate(iter::CartesianPartition)
165,028✔
623
        isempty(iter) && return nothing
165,028✔
624
        f = first(iter)
165,028✔
625
        return (f, (f, 1))
165,028✔
626
    end
627
    @inline function iterate(iter::CartesianPartition, (state, n))
1,467,300✔
628
        n >= length(iter) && return nothing
1,467,300✔
629
        I = IteratorsMD.inc(state.I, iter.parent.parent.indices)
1,245,352✔
630
        return I, (I, n+1)
1,184,120✔
631
    end
632

633
    @inline function simd_outer_range(iter::CartesianPartition)
118,152✔
634
        # In general, the Cartesian Partition might start and stop in the middle of the outer
635
        # dimensions — thus the outer range of a CartesianPartition is itself a
636
        # CartesianPartition.
637
        mi = iter.parent.mi
118,152✔
638
        ci = iter.parent.parent
118,152✔
639
        ax, ax1 = axes(ci), Base.axes1(ci)
118,152✔
640
        subs = Base.ind2sub_rs(ax, mi, first(iter.indices[1]))
118,152✔
641
        vl, fl = Base._sub2ind(tail(ax), tail(subs)...), subs[1]
118,152✔
642
        vr, fr = divrem(last(iter.indices[1]) - 1, mi[end]) .+ (1, first(ax1))
118,152✔
643
        oci = CartesianIndices(tail(ci.indices))
118,152✔
644
        # A fake CartesianPartition to reuse the outer iterate fallback
645
        outer = @inbounds view(ReshapedArray(oci, (length(oci),), mi), vl:vr)
118,152✔
646
        init = @inbounds dec(oci[tail(subs)...].I, oci.indices) # real init state
124,181✔
647
        # Use Generator to make inner loop branchless
648
        @inline function skip_len_I(i::Int, I::CartesianIndex)
313,140✔
649
            l = i == 1 ? fl : first(ax1)
220,638✔
650
            r = i == length(outer) ? fr : last(ax1)
271,824✔
651
            l - first(ax1), r - l + 1, I
194,988✔
652
        end
653
        (skip_len_I(i, I) for (i, I) in Iterators.enumerate(Iterators.rest(outer, (init, 0))))
118,152✔
654
    end
655
    @inline function simd_outer_range(iter::CartesianPartition{CartesianIndex{2}})
5,891✔
656
        # But for two-dimensional Partitions the above is just a simple one-dimensional range
657
        # over the second dimension; we don't need to worry about non-rectangular staggers in
658
        # higher dimensions.
659
        mi = iter.parent.mi
5,891✔
660
        ci = iter.parent.parent
5,891✔
661
        ax, ax1 = axes(ci), Base.axes1(ci)
5,891✔
662
        fl, vl = Base.ind2sub_rs(ax, mi, first(iter.indices[1]))
5,891✔
663
        fr, vr = Base.ind2sub_rs(ax, mi, last(iter.indices[1]))
5,891✔
664
        outer = @inbounds CartesianIndices((ci.indices[2][vl:vr],))
5,891✔
665
        # Use Generator to make inner loop branchless
666
        @inline function skip_len_I(I::CartesianIndex{1})
14,690✔
667
            l = I == first(outer) ? fl : first(ax1)
11,852✔
668
            r = I == last(outer) ? fr : last(ax1)
11,707✔
669
            l - first(ax1), r - l + 1, I
8,799✔
670
        end
671
        (skip_len_I(I) for I in outer)
5,891✔
672
    end
673
    @inline simd_inner_length(iter::CartesianPartition, (_, len, _)::Tuple{Int,Int,CartesianIndex}) = len
203,787✔
674
    @propagate_inbounds simd_index(iter::CartesianPartition, (skip, _, I)::Tuple{Int,Int,CartesianIndex}, n::Int) =
1,333,509✔
675
        simd_index(iter.parent.parent, I, n + skip)
676
end  # IteratorsMD
677

678

679
using .IteratorsMD
680

681
## Bounds-checking with CartesianIndex
682
# Disallow linear indexing with CartesianIndex
683
function checkbounds(::Type{Bool}, A::AbstractArray, i::Union{CartesianIndex, AbstractArray{<:CartesianIndex}})
1,991✔
684
    @inline
1,991✔
685
    checkbounds_indices(Bool, axes(A), (i,))
2,041✔
686
end
687

688
@inline checkbounds_indices(::Type{Bool}, ::Tuple{}, I::Tuple{CartesianIndex,Vararg{Any}}) =
×
689
    checkbounds_indices(Bool, (), (I[1].I..., tail(I)...))
690
@inline checkbounds_indices(::Type{Bool}, IA::Tuple{Any}, I::Tuple{CartesianIndex,Vararg{Any}}) =
905✔
691
    checkbounds_indices(Bool, IA, (I[1].I..., tail(I)...))
692
@inline checkbounds_indices(::Type{Bool}, IA::Tuple, I::Tuple{CartesianIndex,Vararg{Any}}) =
36,858,695✔
693
    checkbounds_indices(Bool, IA, (I[1].I..., tail(I)...))
694

695
# Indexing into Array with mixtures of Integers and CartesianIndices is
696
# extremely performance-sensitive. While the abstract fallbacks support this,
697
# codegen has extra support for SIMDification that sub2ind doesn't (yet) support
698
@propagate_inbounds getindex(A::Array, i1::Union{Integer, CartesianIndex}, I::Union{Integer, CartesianIndex}...) =
72,852,488✔
699
    A[to_indices(A, (i1, I...))...]
700
@propagate_inbounds setindex!(A::Array, v, i1::Union{Integer, CartesianIndex}, I::Union{Integer, CartesianIndex}...) =
65,081,171✔
701
    (A[to_indices(A, (i1, I...))...] = v; A)
40,284,407✔
702

703
# Support indexing with an array of CartesianIndex{N}s
704
# Here we try to consume N of the indices (if there are that many available)
705
# The first two simply handle ambiguities
706
@inline function checkbounds_indices(::Type{Bool}, ::Tuple{},
46✔
707
        I::Tuple{AbstractArray{CartesianIndex{N}},Vararg{Any}}) where N
708
    checkindex(Bool, (), I[1]) & checkbounds_indices(Bool, (), tail(I))
70✔
709
end
710
@inline function checkbounds_indices(::Type{Bool}, IA::Tuple{Any},
30✔
711
        I::Tuple{AbstractArray{CartesianIndex{0}},Vararg{Any}})
712
    checkbounds_indices(Bool, IA, tail(I))
30✔
713
end
714
@inline function checkbounds_indices(::Type{Bool}, IA::Tuple{Any},
7✔
715
        I::Tuple{AbstractArray{CartesianIndex{N}},Vararg{Any}}) where N
716
    checkindex(Bool, IA, I[1]) & checkbounds_indices(Bool, (), tail(I))
7✔
717
end
718
@inline function checkbounds_indices(::Type{Bool}, IA::Tuple,
157✔
719
        I::Tuple{AbstractArray{CartesianIndex{N}},Vararg{Any}}) where N
720
    IA1, IArest = IteratorsMD.split(IA, Val(N))
157✔
721
    checkindex(Bool, IA1, I[1]) & checkbounds_indices(Bool, IArest, tail(I))
277✔
722
end
723

724

725
@inline function checkbounds_indices(::Type{Bool}, IA::Tuple{},
5✔
726
    I::Tuple{AbstractArray{Bool,N},Vararg{Any}}) where N
727
    return checkbounds_indices(Bool, IA, (LogicalIndex(I[1]), tail(I)...))
6✔
728
end
729
@inline function checkbounds_indices(::Type{Bool}, IA::Tuple,
48✔
730
    I::Tuple{AbstractArray{Bool,N},Vararg{Any}}) where N
731
    return checkbounds_indices(Bool, IA, (LogicalIndex(I[1]), tail(I)...))
73✔
732
end
733

734
function checkindex(::Type{Bool}, inds::Tuple, I::AbstractArray{<:CartesianIndex})
147✔
735
    b = true
147✔
736
    for i in I
149✔
737
        b &= checkbounds_indices(Bool, inds, (i,))
577✔
738
    end
744✔
739
    b
147✔
740
end
741
checkindex(::Type{Bool}, inds::Tuple, I::CartesianIndices) = all(checkindex.(Bool, inds, I.indices))
35✔
742

743
# combined count of all indices, including CartesianIndex and
744
# AbstractArray{CartesianIndex}
745
# rather than returning N, it returns an NTuple{N,Bool} so the result is inferable
746
@inline index_ndims(i1, I...) = (true, index_ndims(I...)...)
613,074✔
747
@inline function index_ndims(i1::CartesianIndex, I...)
×
748
    (map(Returns(true), i1.I)..., index_ndims(I...)...)
×
749
end
750
@inline function index_ndims(i1::AbstractArray{CartesianIndex{N}}, I...) where N
177✔
751
    (ntuple(Returns(true), Val(N))..., index_ndims(I...)...)
177✔
752
end
753
index_ndims() = ()
×
754

755
# combined dimensionality of all indices
756
# rather than returning N, it returns an NTuple{N,Bool} so the result is inferable
757
@inline index_dimsum(i1, I...) = (index_dimsum(I...)...,)
144,412✔
758
@inline index_dimsum(::Colon, I...) = (true, index_dimsum(I...)...)
×
759
@inline index_dimsum(::AbstractArray{Bool}, I...) = (true, index_dimsum(I...)...)
×
760
@inline function index_dimsum(::AbstractArray{<:Any,N}, I...) where N
162,590✔
761
    (ntuple(Returns(true), Val(N))..., index_dimsum(I...)...)
162,590✔
762
end
763
index_dimsum() = ()
×
764

765
# Recursively compute the lengths of a list of indices, without dropping scalars
766
index_lengths() = ()
×
767
@inline index_lengths(::Real, rest...) = (1, index_lengths(rest...)...)
2,501✔
768
@inline index_lengths(A::AbstractArray, rest...) = (length(A), index_lengths(rest...)...)
15,458✔
769

770
# shape of array to create for getindex() with indices I, dropping scalars
771
# returns a Tuple{Vararg{AbstractUnitRange}} of indices
772
index_shape() = ()
×
773
@inline index_shape(::Real, rest...) = index_shape(rest...)
6,596✔
774
@inline index_shape(A::AbstractArray, rest...) = (axes(A)..., index_shape(rest...)...)
127,060✔
775

776
"""
777
    LogicalIndex(mask)
778

779
The `LogicalIndex` type is a special vector that simply contains all indices I
780
where `mask[I]` is true. This specialized type does not support indexing
781
directly as doing so would require O(n) lookup time. `AbstractArray{Bool}` are
782
wrapped with `LogicalIndex` upon calling [`to_indices`](@ref).
783
"""
784
struct LogicalIndex{T, A<:AbstractArray{Bool}} <: AbstractVector{T}
785
    mask::A
786
    sum::Int
787
    LogicalIndex{T,A}(mask::A) where {T,A<:AbstractArray{Bool}} = new(mask, count(mask))
14,454✔
788
end
789
LogicalIndex(mask::AbstractVector{Bool}) = LogicalIndex{Int, typeof(mask)}(mask)
619✔
790
LogicalIndex(mask::AbstractArray{Bool, N}) where {N} = LogicalIndex{CartesianIndex{N}, typeof(mask)}(mask)
48✔
791
LogicalIndex{Int}(mask::AbstractArray) = LogicalIndex{Int, typeof(mask)}(mask)
13,787✔
792
size(L::LogicalIndex) = (L.sum,)
3,597✔
793
length(L::LogicalIndex) = L.sum
1,613✔
794
collect(L::LogicalIndex) = [i for i in L]
577✔
795
show(io::IO, r::LogicalIndex) = print(io,collect(r))
×
796
print_array(io::IO, X::LogicalIndex) = print_array(io, collect(X))
×
797
# Iteration over LogicalIndex is very performance-critical, but it also must
798
# support arbitrary AbstractArray{Bool}s with both Int and CartesianIndex.
799
# Thus the iteration state contains an index iterator and its state. We also
800
# keep track of the count of elements since we already know how many there
801
# should be -- this way we don't need to look at future indices to check done.
802
@inline function iterate(L::LogicalIndex{Int})
51✔
803
    r = LinearIndices(L.mask)
51✔
804
    iterate(L, (1, r))
85✔
805
end
806
@inline function iterate(L::LogicalIndex{<:CartesianIndex})
7✔
807
    r = CartesianIndices(axes(L.mask))
7✔
808
    iterate(L, (1, r))
14✔
809
end
810
@propagate_inbounds function iterate(L::LogicalIndex, s)
1,512✔
811
    # We're looking for the n-th true element, using iterator r at state i
812
    n = s[1]
1,512✔
813
    n > length(L) && return nothing
1,512✔
814
    #unroll once to help inference, cf issue #29418
815
    idx, i = iterate(tail(s)...)
2,908✔
816
    s = (n+1, s[2], i)
1,454✔
817
    L.mask[idx] && return (idx, s)
1,454✔
818
    while true
6,100✔
819
        idx, i = iterate(tail(s)...)
12,200✔
820
        s = (n+1, s[2], i)
6,100✔
821
        L.mask[idx] && return (idx, s)
6,100✔
822
    end
4,973✔
823
end
824
# When wrapping a BitArray, lean heavily upon its internals.
825
@inline function iterate(L::LogicalIndex{Int,<:BitArray})
707✔
826
    L.sum == 0 && return nothing
4,997✔
827
    Bc = L.mask.chunks
4,619✔
828
    return iterate(L, (1, 1, (), @inbounds Bc[1]))
4,619✔
829
end
830
@inline function iterate(L::LogicalIndex{<:CartesianIndex,<:BitArray})
23✔
831
    L.sum == 0 && return nothing
23✔
832
    Bc = L.mask.chunks
23✔
833
    irest = ntuple(one, ndims(L.mask)-1)
23✔
834
    return iterate(L, (1, 1, irest, @inbounds Bc[1]))
23✔
835
end
836
@inline function iterate(L::LogicalIndex{<:Any,<:BitArray}, (i1, Bi, irest, c))
6,868✔
837
    Bc = L.mask.chunks
37,996✔
838
    while c == 0
48,667✔
839
        Bi >= length(Bc) && return nothing
15,313✔
840
        i1 += 64
10,671✔
841
        @inbounds c = Bc[Bi+=1]
10,671✔
842
    end
10,671✔
843
    tz = trailing_zeros(c)
33,354✔
844
    c = _blsr(c)
33,354✔
845
    i1, irest = _overflowind(i1 + tz, irest, size(L.mask))
33,354✔
846
    return eltype(L)(i1, irest...), (i1 - tz, Bi, irest, c)
33,354✔
847
end
848

849
@inline checkbounds(::Type{Bool}, A::AbstractArray, I::LogicalIndex{<:Any,<:AbstractArray{Bool,1}}) =
2,575✔
850
    eachindex(IndexLinear(), A) == eachindex(IndexLinear(), I.mask)
851
@inline checkbounds(::Type{Bool}, A::AbstractArray, I::LogicalIndex) = axes(A) == axes(I.mask)
73✔
852
@inline checkindex(::Type{Bool}, indx::AbstractUnitRange, I::LogicalIndex) = (indx,) == axes(I.mask)
2,050✔
853
checkindex(::Type{Bool}, inds::Tuple, I::LogicalIndex) = checkbounds_indices(Bool, inds, axes(I.mask))
28✔
854

855
ensure_indexable(I::Tuple{}) = ()
×
856
@inline ensure_indexable(I::Tuple{Any, Vararg{Any}}) = (I[1], ensure_indexable(tail(I))...)
4,032,833✔
857
@inline ensure_indexable(I::Tuple{LogicalIndex, Vararg{Any}}) = (collect(I[1]), ensure_indexable(tail(I))...)
577✔
858

859
# In simple cases, we know that we don't need to use axes(A). Optimize those
860
# until Julia gets smart enough to elide the call on its own:
861
@inline to_indices(A, I::Tuple{Vararg{Union{Integer, CartesianIndex}}}) = to_indices(A, (), I)
209,066,234✔
862
# But some index types require more context spanning multiple indices
863
# CartesianIndex is unfolded outside the inner to_indices for better inference
864
_to_indices1(A, inds, I1::CartesianIndex) = map(Fix1(to_index, A), I1.I)
209,067,480✔
865
_cutdim(inds, I1::CartesianIndex) = IteratorsMD.split(inds, Val(length(I1)))[2]
205,844,592✔
866
# For arrays of CartesianIndex, we just skip the appropriate number of inds
867
_cutdim(inds, I1::AbstractArray{CartesianIndex{N}}) where {N} = IteratorsMD.split(inds, Val(N))[2]
160✔
868
# And boolean arrays behave similarly; they also skip their number of dimensions
869
_cutdim(inds::Tuple, I1::AbstractArray{Bool}) = IteratorsMD.split(inds, Val(ndims(I1)))[2]
213✔
870
# As an optimization, we allow trailing Array{Bool} and BitArray to be linear over trailing dimensions
871
@inline to_indices(A, inds, I::Tuple{Union{Array{Bool,N}, BitArray{N}}}) where {N} =
13,870✔
872
    (_maybe_linear_logical_index(IndexStyle(A), A, I[1]),)
873
_maybe_linear_logical_index(::IndexStyle, A, i) = to_index(A, i)
83✔
874
_maybe_linear_logical_index(::IndexLinear, A, i) = LogicalIndex{Int}(i)
13,787✔
875

876
# Colons get converted to slices by `uncolon`
877
_to_indices1(A, inds, I1::Colon) = (uncolon(inds),)
78,866✔
878

879
uncolon(::Tuple{}) = Slice(OneTo(1))
257✔
880
uncolon(inds::Tuple) = Slice(inds[1])
78,539✔
881

882
### From abstractarray.jl: Internal multidimensional indexing definitions ###
883
getindex(x::Union{Number,AbstractChar}, ::CartesianIndex{0}) = x
99✔
884
getindex(t::Tuple,  i::CartesianIndex{1}) = getindex(t, i.I[1])
×
885

886
# These are not defined on directly on getindex to avoid
887
# ambiguities for AbstractArray subtypes. See the note in abstractarray.jl
888

889
@inline function _getindex(l::IndexStyle, A::AbstractArray, I::Union{Real, AbstractArray}...)
37,747✔
890
    @boundscheck checkbounds(A, I...)
105,648✔
891
    return _unsafe_getindex(l, _maybe_reshape(l, A, I...), I...)
412,267✔
892
end
893
# But we can speed up IndexCartesian arrays by reshaping them to the appropriate dimensionality:
894
_maybe_reshape(::IndexLinear, A::AbstractArray, I...) = A
36,002✔
895
_maybe_reshape(::IndexCartesian, A::AbstractVector, I...) = A
578✔
896
@inline _maybe_reshape(::IndexCartesian, A::AbstractArray, I...) = __maybe_reshape(A, index_ndims(I...))
7,501✔
897
@inline __maybe_reshape(A::AbstractArray{T,N}, ::NTuple{N,Any}) where {T,N} = A
2,704✔
898
@inline __maybe_reshape(A::AbstractArray, ::NTuple{N,Any}) where {N} = reshape(A, Val(N))
4,797✔
899

900
function _unsafe_getindex(::IndexStyle, A::AbstractArray, I::Vararg{Union{Real, AbstractArray}, N}) where N
105,565✔
901
    # This is specifically not inlined to prevent excessive allocations in type unstable code
902
    shape = index_shape(I...)
105,565✔
903
    dest = similar(A, shape)
105,635✔
904
    map(length, axes(dest)) == map(length, shape) || throw_checksize_error(dest, shape)
105,565✔
905
    _unsafe_getindex!(dest, A, I...) # usually a generated function, don't allow it to impact inference result
3,121,986✔
906
    return dest
105,564✔
907
end
908

909
function _generate_unsafe_getindex!_body(N::Int)
281✔
910
    quote
281✔
911
        @inline
46,976✔
912
        D = eachindex(dest)
113,532✔
913
        Dy = iterate(D)
226,242✔
914
        @inbounds @nloops $N j d->I[d] begin
4,949,870✔
915
            # This condition is never hit, but at the moment
916
            # the optimizer is not clever enough to split the union without it
917
            Dy === nothing && return dest
3,114,710✔
918
            (idx, state) = Dy
1,474,755✔
919
            dest[idx] = @ncall $N getindex src j
3,187,605✔
920
            Dy = iterate(D, state)
6,115,161✔
921
        end
922
        return dest
113,531✔
923
    end
924
end
925

926
# Always index with the exactly indices provided.
927
@generated function _unsafe_getindex!(dest::AbstractArray, src::AbstractArray, I::Vararg{Union{Real, AbstractArray}, N}) where N
284,944✔
928
    _generate_unsafe_getindex!_body(N)
281✔
929
end
930

931
# manually written-out specializations for 1 and 2 arguments to save compile time
932
@eval function _unsafe_getindex!(dest::AbstractArray, src::AbstractArray, I::Vararg{Union{Real, AbstractArray},1})
8,777✔
933
    $(_generate_unsafe_getindex!_body(1))
8,777✔
934
end
935

936
@eval function _unsafe_getindex!(dest::AbstractArray, src::AbstractArray, I::Vararg{Union{Real, AbstractArray},2})
22,870✔
937
    $(_generate_unsafe_getindex!_body(2))
22,870✔
938
end
939

940
@noinline throw_checksize_error(A, sz) = throw(DimensionMismatch("output array is the wrong size; expected $sz, got $(size(A))"))
×
941

942
## setindex! ##
943
function _setindex!(l::IndexStyle, A::AbstractArray, x, I::Union{Real, AbstractArray}...)
7,419✔
944
    @inline
7,419✔
945
    @boundscheck checkbounds(A, I...)
7,419✔
946
    _unsafe_setindex!(l, _maybe_reshape(l, A, I...), x, I...)
30,274✔
947
    A
7,412✔
948
end
949

950
function _generate_unsafe_setindex!_body(N::Int)
71✔
951
    quote
71✔
952
        x′ = unalias(A, x)
13,199✔
953
        @nexprs $N d->(I_d = unalias(A, I[d]))
8,323✔
954
        idxlens = @ncall $N index_lengths I
7,419✔
955
        @ncall $N setindex_shape_check x′ (d->idxlens[d])
11,531✔
956
        Xy = iterate(x′)
9,970✔
957
        @inbounds @nloops $N i d->I_d begin
104,373✔
958
            # This is never reached, but serves as an assumption for
959
            # the optimizer that it does not need to emit error paths
960
            Xy === nothing && break
624,210✔
961
            (val, state) = Xy
624,210✔
962
            @ncall $N setindex! A val i
656,280✔
963
            Xy = iterate(x′, state)
713,836✔
964
        end
965
        A
7,412✔
966
    end
967
end
968

969
@generated function _unsafe_setindex!(::IndexStyle, A::AbstractArray, x, I::Vararg{Union{Real,AbstractArray}, N}) where N
1,126✔
970
    _generate_unsafe_setindex!_body(N)
71✔
971
end
972

973
@eval function _unsafe_setindex!(::IndexStyle, A::AbstractArray, x, I::Vararg{Union{Real,AbstractArray},1})
1,051✔
974
    $(_generate_unsafe_setindex!_body(1))
1,051✔
975
end
976

977
@eval function _unsafe_setindex!(::IndexStyle, A::AbstractArray, x, I::Vararg{Union{Real,AbstractArray},2})
5,805✔
978
    $(_generate_unsafe_setindex!_body(2))
5,805✔
979
end
980

981
diff(a::AbstractVector) = diff(a, dims=1)
18✔
982

983
"""
984
    diff(A::AbstractVector)
985
    diff(A::AbstractArray; dims::Integer)
986

987
Finite difference operator on a vector or a multidimensional array `A`. In the
988
latter case the dimension to operate on needs to be specified with the `dims`
989
keyword argument.
990

991
!!! compat "Julia 1.1"
992
    `diff` for arrays with dimension higher than 2 requires at least Julia 1.1.
993

994
# Examples
995
```jldoctest
996
julia> a = [2 4; 6 16]
997
2×2 Matrix{Int64}:
998
 2   4
999
 6  16
1000

1001
julia> diff(a, dims=2)
1002
2×1 Matrix{Int64}:
1003
  2
1004
 10
1005

1006
julia> diff(vec(a))
1007
3-element Vector{Int64}:
1008
  4
1009
 -2
1010
 12
1011
```
1012
"""
1013
function diff(a::AbstractArray{T,N}; dims::Integer) where {T,N}
77✔
1014
    require_one_based_indexing(a)
38✔
1015
    1 <= dims <= N || throw(ArgumentError("dimension $dims out of range (1:$N)"))
40✔
1016

1017
    r = axes(a)
36✔
1018
    r0 = ntuple(i -> i == dims ? UnitRange(1, last(r[i]) - 1) : UnitRange(r[i]), N)
93✔
1019
    r1 = ntuple(i -> i == dims ? UnitRange(2, last(r[i])) : UnitRange(r[i]), N)
93✔
1020

1021
    return view(a, r1...) .- view(a, r0...)
36✔
1022
end
1023
function diff(r::AbstractRange{T}; dims::Integer=1) where {T}
30✔
1024
    dims == 1 || throw(ArgumentError("dimension $dims out of range (1:1)"))
19✔
1025
    return [@inbounds r[i+1] - r[i] for i in firstindex(r):lastindex(r)-1]
11✔
1026
end
1027

1028
### from abstractarray.jl
1029

1030
# In the common case where we have two views into the same parent, aliasing checks
1031
# are _much_ easier and more important to get right
1032
function mightalias(A::SubArray{T,<:Any,P}, B::SubArray{T,<:Any,P}) where {T,P}
934✔
1033
    if !_parentsmatch(A.parent, B.parent)
1,486✔
1034
        # We cannot do any better than the usual dataids check
1035
        return !_isdisjoint(dataids(A), dataids(B))
382✔
1036
    end
1037
    # Now we know that A.parent === B.parent. This means that the indices of A
1038
    # and B are the same length and indexing into the same dimensions. We can
1039
    # just walk through them and check for overlaps: O(ndims(A)). We must finally
1040
    # ensure that the indices don't alias with either parent
1041
    return _indicesmightoverlap(A.indices, B.indices) ||
552✔
1042
        !_isdisjoint(dataids(A.parent), _splatmap(dataids, B.indices)) ||
1043
        !_isdisjoint(dataids(B.parent), _splatmap(dataids, A.indices))
1044
end
1045
_parentsmatch(A::AbstractArray, B::AbstractArray) = A === B
×
1046
# Two reshape(::Array)s of the same size aren't `===` because they have different headers
1047
_parentsmatch(A::Array, B::Array) = pointer(A) == pointer(B) && size(A) == size(B)
1,486✔
1048

1049
_indicesmightoverlap(A::Tuple{}, B::Tuple{}) = true
×
1050
_indicesmightoverlap(A::Tuple{}, B::Tuple) = error("malformed subarray")
×
1051
_indicesmightoverlap(A::Tuple, B::Tuple{}) = error("malformed subarray")
×
1052
# For ranges, it's relatively cheap to construct the intersection
1053
@inline function _indicesmightoverlap(A::Tuple{AbstractRange, Vararg{Any}}, B::Tuple{AbstractRange, Vararg{Any}})
×
1054
    !isempty(intersect(A[1], B[1])) ? _indicesmightoverlap(tail(A), tail(B)) : false
×
1055
end
1056
# But in the common AbstractUnitRange case, there's an even faster shortcut
1057
@inline function _indicesmightoverlap(A::Tuple{AbstractUnitRange, Vararg{Any}}, B::Tuple{AbstractUnitRange, Vararg{Any}})
554✔
1058
    max(first(A[1]),first(B[1])) <= min(last(A[1]),last(B[1])) ? _indicesmightoverlap(tail(A), tail(B)) : false
556✔
1059
end
1060
# And we can check scalars against each other and scalars against arrays quite easily
1061
@inline _indicesmightoverlap(A::Tuple{Real, Vararg{Any}}, B::Tuple{Real, Vararg{Any}}) =
538✔
1062
    A[1] == B[1] ? _indicesmightoverlap(tail(A), tail(B)) : false
1063
@inline _indicesmightoverlap(A::Tuple{Real, Vararg{Any}}, B::Tuple{AbstractArray, Vararg{Any}}) =
1✔
1064
    A[1] in B[1] ? _indicesmightoverlap(tail(A), tail(B)) : false
1065
@inline _indicesmightoverlap(A::Tuple{AbstractArray, Vararg{Any}}, B::Tuple{Real, Vararg{Any}}) =
1✔
1066
    B[1] in A[1] ? _indicesmightoverlap(tail(A), tail(B)) : false
1067
# And small arrays are quick, too
1068
@inline function _indicesmightoverlap(A::Tuple{AbstractArray, Vararg{Any}}, B::Tuple{AbstractArray, Vararg{Any}})
×
1069
    if length(A[1]) == 1
×
1070
        return A[1][1] in B[1] ? _indicesmightoverlap(tail(A), tail(B)) : false
×
1071
    elseif length(B[1]) == 1
×
1072
        return B[1][1] in A[1] ? _indicesmightoverlap(tail(A), tail(B)) : false
×
1073
    else
1074
        # But checking larger arrays requires O(m*n) and is too much work
1075
        return true
×
1076
    end
1077
end
1078
# And in general, checking the intersection is too much work
1079
_indicesmightoverlap(A::Tuple{Any, Vararg{Any}}, B::Tuple{Any, Vararg{Any}}) = true
×
1080

1081
"""
1082
    fill!(A, x)
1083

1084
Fill array `A` with the value `x`. If `x` is an object reference, all elements will refer to
1085
the same object. `fill!(A, Foo())` will return `A` filled with the result of evaluating
1086
`Foo()` once.
1087

1088
# Examples
1089
```jldoctest
1090
julia> A = zeros(2,3)
1091
2×3 Matrix{Float64}:
1092
 0.0  0.0  0.0
1093
 0.0  0.0  0.0
1094

1095
julia> fill!(A, 2.)
1096
2×3 Matrix{Float64}:
1097
 2.0  2.0  2.0
1098
 2.0  2.0  2.0
1099

1100
julia> a = [1, 1, 1]; A = fill!(Vector{Vector{Int}}(undef, 3), a); a[1] = 2; A
1101
3-element Vector{Vector{Int64}}:
1102
 [2, 1, 1]
1103
 [2, 1, 1]
1104
 [2, 1, 1]
1105

1106
julia> x = 0; f() = (global x += 1; x); fill!(Vector{Int}(undef, 3), f())
1107
3-element Vector{Int64}:
1108
 1
1109
 1
1110
 1
1111
```
1112
"""
1113
function fill!(A::AbstractArray{T}, x) where T
60,171✔
1114
    xT = convert(T, x)
60,171✔
1115
    for I in eachindex(A)
180,392✔
1116
        @inbounds A[I] = xT
46,344,562✔
1117
    end
92,561,131✔
1118
    A
123,676✔
1119
end
1120

1121
function copyto!(dest::AbstractArray{T1,N}, Rdest::CartesianIndices{N},
494✔
1122
                  src::AbstractArray{T2,N}, Rsrc::CartesianIndices{N}) where {T1,T2,N}
1123
    isempty(Rdest) && return dest
494✔
1124
    if size(Rdest) != size(Rsrc)
914✔
1125
        throw(ArgumentError("source and destination must have same size (got $(size(Rsrc)) and $(size(Rdest)))"))
×
1126
    end
1127
    checkbounds(dest, first(Rdest))
457✔
1128
    checkbounds(dest, last(Rdest))
457✔
1129
    checkbounds(src, first(Rsrc))
457✔
1130
    checkbounds(src, last(Rsrc))
457✔
1131
    src′ = unalias(dest, src)
776✔
1132
    CRdest = CartesianIndices(Rdest)
457✔
1133
    CRsrc = CartesianIndices(Rsrc)
457✔
1134
    ΔI = first(CRdest) - first(CRsrc)
457✔
1135
    if @generated
×
1136
        quote
78✔
1137
            @nloops $N i (n->CRsrc.indices[n]) begin
11,698✔
1138
                @inbounds @nref($N,dest,n->Rdest.indices[n][i_n+ΔI[n]]) = @nref($N,src′,n->Rsrc.indices[n][i_n])
23,919✔
1139
            end
1140
        end
1141
    else
1142
        for I in CRsrc
1143
            @inbounds dest[Rdest[I + ΔI]] = src′[Rsrc[I]]
1144
        end
1145
    end
1146
    dest
457✔
1147
end
1148

1149
"""
1150
    copyto!(dest, Rdest::CartesianIndices, src, Rsrc::CartesianIndices) -> dest
1151

1152
Copy the block of `src` in the range of `Rsrc` to the block of `dest`
1153
in the range of `Rdest`. The sizes of the two regions must match.
1154

1155
# Examples
1156
```jldoctest
1157
julia> A = zeros(5, 5);
1158

1159
julia> B = [1 2; 3 4];
1160

1161
julia> Ainds = CartesianIndices((2:3, 2:3));
1162

1163
julia> Binds = CartesianIndices(B);
1164

1165
julia> copyto!(A, Ainds, B, Binds)
1166
5×5 Matrix{Float64}:
1167
 0.0  0.0  0.0  0.0  0.0
1168
 0.0  1.0  2.0  0.0  0.0
1169
 0.0  3.0  4.0  0.0  0.0
1170
 0.0  0.0  0.0  0.0  0.0
1171
 0.0  0.0  0.0  0.0  0.0
1172
```
1173
"""
1174
copyto!(::AbstractArray, ::CartesianIndices, ::AbstractArray, ::CartesianIndices)
1175

1176
# circshift!
1177
circshift!(dest::AbstractArray, src, ::Tuple{}) = copyto!(dest, src)
×
1178
"""
1179
    circshift!(dest, src, shifts)
1180

1181
Circularly shift, i.e. rotate, the data in `src`, storing the result in
1182
`dest`. `shifts` specifies the amount to shift in each dimension.
1183

1184
The `dest` array must be distinct from the `src` array (they cannot
1185
alias each other).
1186

1187
See also [`circshift`](@ref).
1188
"""
1189
@noinline function circshift!(dest::AbstractArray{T,N}, src, shiftamt::DimsInteger) where {T,N}
142✔
1190
    dest === src && throw(ArgumentError("dest and src must be separate arrays"))
142✔
1191
    inds = axes(src)
141✔
1192
    axes(dest) == inds || throw(ArgumentError("indices of src and dest must match (got $inds and $(axes(dest)))"))
141✔
1193
    isempty(src) && return dest
141✔
1194
    _circshift!(dest, (), src, (), inds, fill_to_length(shiftamt, 0, Val(N)))
139✔
1195
end
1196

1197
circshift!(dest::AbstractArray, src, shiftamt) =
9✔
1198
    circshift!(dest, src, map(Integer, (shiftamt...,)))
1199

1200
# For each dimension, we copy the first half of src to the second half
1201
# of dest, and the second half of src to the first half of dest. This
1202
# uses a recursive bifurcation strategy so that these splits can be
1203
# encoded by ranges, which means that we need only one call to `mod`
1204
# per dimension rather than one call per index.
1205
# `rdest` and `rsrc` are tuples-of-ranges that grow one dimension at a
1206
# time; when all the dimensions have been filled in, you call `copyto!`
1207
# for that block. In other words, in two dimensions schematically we
1208
# have the following call sequence (--> means a call):
1209
#   circshift!(dest, src, shiftamt) -->
1210
#     _circshift!(dest, src, ("first half of dim1",)) -->
1211
#       _circshift!(dest, src, ("first half of dim1", "first half of dim2")) --> copyto!
1212
#       _circshift!(dest, src, ("first half of dim1", "second half of dim2")) --> copyto!
1213
#     _circshift!(dest, src, ("second half of dim1",)) -->
1214
#       _circshift!(dest, src, ("second half of dim1", "first half of dim2")) --> copyto!
1215
#       _circshift!(dest, src, ("second half of dim1", "second half of dim2")) --> copyto!
1216
@inline function _circshift!(dest, rdest, src, rsrc,
227✔
1217
                             inds::Tuple{AbstractUnitRange,Vararg{Any}},
1218
                             shiftamt::Tuple{Integer,Vararg{Any}})::typeof(dest)
1219
    ind1, d = inds[1], shiftamt[1]
227✔
1220
    s = mod(d, length(ind1))
454✔
1221
    sf, sl = first(ind1)+s, last(ind1)-s
227✔
1222
    r1, r2 = first(ind1):sf-1, sf:last(ind1)
227✔
1223
    r3, r4 = first(ind1):sl, sl+1:last(ind1)
255✔
1224
    tinds, tshiftamt = tail(inds), tail(shiftamt)
227✔
1225
    _circshift!(dest, (rdest..., r1), src, (rsrc..., r4), tinds, tshiftamt)
235✔
1226
    _circshift!(dest, (rdest..., r2), src, (rsrc..., r3), tinds, tshiftamt)
235✔
1227
end
1228
# At least one of inds, shiftamt is empty
1229
function _circshift!(dest, rdest, src, rsrc, inds, shiftamt)
366✔
1230
    copyto!(dest, CartesianIndices(rdest), src, CartesianIndices(rsrc))
366✔
1231
end
1232

1233
# circcopy!
1234
"""
1235
    circcopy!(dest, src)
1236

1237
Copy `src` to `dest`, indexing each dimension modulo its length.
1238
`src` and `dest` must have the same size, but can be offset in
1239
their indices; any offset results in a (circular) wraparound. If the
1240
arrays have overlapping indices, then on the domain of the overlap
1241
`dest` agrees with `src`.
1242

1243
See also: [`circshift`](@ref).
1244

1245
# Examples
1246
```julia-repl
1247
julia> src = reshape(Vector(1:16), (4,4))
1248
4×4 Array{Int64,2}:
1249
 1  5   9  13
1250
 2  6  10  14
1251
 3  7  11  15
1252
 4  8  12  16
1253

1254
julia> dest = OffsetArray{Int}(undef, (0:3,2:5))
1255

1256
julia> circcopy!(dest, src)
1257
OffsetArrays.OffsetArray{Int64,2,Array{Int64,2}} with indices 0:3×2:5:
1258
 8  12  16  4
1259
 5   9  13  1
1260
 6  10  14  2
1261
 7  11  15  3
1262

1263
julia> dest[1:3,2:4] == src[1:3,2:4]
1264
true
1265
```
1266
"""
1267
function circcopy!(dest, src)
2✔
1268
    dest === src && throw(ArgumentError("dest and src must be separate arrays"))
2✔
1269
    indssrc, indsdest = axes(src), axes(dest)
2✔
1270
    if (szsrc = map(length, indssrc)) != (szdest = map(length, indsdest))
4✔
1271
        throw(DimensionMismatch("src and dest must have the same sizes (got $szsrc and $szdest)"))
×
1272
    end
1273
    shift = map((isrc, idest)->first(isrc)-first(idest), indssrc, indsdest)
7✔
1274
    all(x->x==0, shift) && return copyto!(dest, src)
7✔
1275
    _circcopy!(dest, (), indsdest, src, (), indssrc)
1✔
1276
end
1277

1278
# This uses the same strategy described above for _circshift!
1279
@inline function _circcopy!(dest, rdest, indsdest::Tuple{AbstractUnitRange,Vararg{Any}},
3✔
1280
                            src,  rsrc,  indssrc::Tuple{AbstractUnitRange,Vararg{Any}})::typeof(dest)
1281
    indd1, inds1 = indsdest[1], indssrc[1]
3✔
1282
    l = length(indd1)
3✔
1283
    s = mod(first(inds1)-first(indd1), l)
6✔
1284
    sdf = first(indd1)+s
3✔
1285
    rd1, rd2 = first(indd1):sdf-1, sdf:last(indd1)
3✔
1286
    ssf = last(inds1)-s
3✔
1287
    rs1, rs2 = first(inds1):ssf, ssf+1:last(inds1)
3✔
1288
    tindsd, tindss = tail(indsdest), tail(indssrc)
3✔
1289
    _circcopy!(dest, (rdest..., rd1), tindsd, src, (rsrc..., rs2), tindss)
3✔
1290
    _circcopy!(dest, (rdest..., rd2), tindsd, src, (rsrc..., rs1), tindss)
3✔
1291
end
1292

1293
# At least one of indsdest, indssrc are empty (and both should be, since we've checked)
1294
function _circcopy!(dest, rdest, indsdest, src, rsrc, indssrc)
4✔
1295
    copyto!(dest, CartesianIndices(rdest), src, CartesianIndices(rsrc))
4✔
1296
end
1297

1298
### BitArrays
1299

1300
## getindex
1301

1302
# contiguous multidimensional indexing: if the first dimension is a range,
1303
# we can get some performance from using copy_chunks!
1304
@inline function _unsafe_getindex!(X::BitArray, B::BitArray, I0::Union{AbstractUnitRange{Int},Slice})
×
1305
    copy_chunks!(X.chunks, 1, B.chunks, indexoffset(I0)+1, length(I0))
×
1306
    return X
×
1307
end
1308

1309
# Optimization where the inner dimension is contiguous improves perf dramatically
1310
@generated function _unsafe_getindex!(X::BitArray, B::BitArray,
2,544✔
1311
        I0::Union{Slice,UnitRange{Int}}, I::Union{Int,AbstractUnitRange{Int},Slice}...)
1312
    N = length(I)
12✔
1313
    quote
12✔
1314
        $(Expr(:meta, :inline))
822✔
1315
        @nexprs $N d->(I_d = I[d])
820✔
1316

1317
        idxlens = @ncall $N index_lengths I0 I
1,204✔
1318

1319
        f0 = indexoffset(I0)+1
922✔
1320
        l0 = idxlens[1]
822✔
1321

1322
        gap_lst_1 = 0
822✔
1323
        @nexprs $N d->(gap_lst_{d+1} = idxlens[d+1])
820✔
1324
        stride = 1
822✔
1325
        ind = f0
822✔
1326
        @nexprs $N d->begin
×
1327
            stride *= size(B, d)
2,742✔
1328
            stride_lst_d = stride
2,460✔
1329
            ind += stride * indexoffset(I_d)
2,742✔
1330
            gap_lst_{d+1} *= stride
2,460✔
1331
        end
1332

1333
        storeind = 1
822✔
1334
        Xc, Bc = X.chunks, B.chunks
1,204✔
1335
        @nloops($N, i, d->(1:idxlens[d+1]),
2,442✔
1336
                d->nothing, # PRE
1337
                d->(ind += stride_lst_d - gap_lst_d), # POST
1338
                begin # BODY
1339
                    copy_chunks!(Xc, storeind, Bc, ind, l0)
3,744✔
1340
                    storeind += l0
3,362✔
1341
                end)
1342
        return X
1,104✔
1343
    end
1344
end
1345

1346
# in the general multidimensional non-scalar case, can we do about 10% better
1347
# in most cases by manually hoisting the bitarray chunks access out of the loop
1348
# (This should really be handled by the compiler or with an immutable BitArray)
1349
@generated function _unsafe_getindex!(X::BitArray, B::BitArray, I::Union{Int,AbstractArray{Int}}...)
6,668✔
1350
    N = length(I)
9✔
1351
    quote
9✔
1352
        $(Expr(:meta, :inline))
1,080✔
1353
        stride_1 = 1
1,080✔
1354
        @nexprs $N d->(stride_{d+1} = stride_d*size(B, d))
1,998✔
1355
        $(Symbol(:offset_, N)) = 1
1,080✔
1356
        ind = 0
1,080✔
1357
        Xc, Bc = X.chunks, B.chunks
2,002✔
1358
        @nloops $N i d->I[d] d->(@inbounds offset_{d-1} = offset_d + (i_d-1)*stride_d) begin
6,668✔
1359
            ind += 1
32,475✔
1360
            unsafe_bitsetindex!(Xc, unsafe_bitgetindex(Bc, offset_0), ind)
32,475✔
1361
        end
1362
        return X
2,002✔
1363
    end
1364
end
1365

1366
## setindex!
1367

1368
function copy_to_bitarray_chunks!(Bc::Vector{UInt64}, pos_d::Int, C::StridedArray, pos_s::Int, numbits::Int)
×
1369
    bind = pos_d
×
1370
    cind = pos_s
×
1371
    lastind = pos_d + numbits - 1
×
1372
    @inbounds while bind ≤ lastind
×
1373
        unsafe_bitsetindex!(Bc, Bool(C[cind]), bind)
×
1374
        bind += 1
×
1375
        cind += 1
×
1376
    end
×
1377
end
1378

1379
# Note: the next two functions rely on the following definition of the conversion to Bool:
1380
#   convert(::Type{Bool}, x::Real) = x==0 ? false : x==1 ? true : throw(InexactError(...))
1381
# they're used to preemptively check in bulk when possible, which is much faster.
1382
# Also, the functions can be overloaded for custom types T<:Real :
1383
#  a) in the unlikely eventuality that they use a different logic for Bool conversion
1384
#  b) to skip the check if not necessary
1385
@inline try_bool_conversion(x::Real) =
×
1386
    x == 0 || x == 1 || throw(InexactError(:try_bool_conversion, Bool, x))
1387
@inline unchecked_bool_convert(x::Real) = x == 1
×
1388

1389
function copy_to_bitarray_chunks!(Bc::Vector{UInt64}, pos_d::Int, C::StridedArray{<:Real}, pos_s::Int, numbits::Int)
×
1390
    @inbounds for i = (1:numbits) .+ (pos_s - 1)
×
1391
        try_bool_conversion(C[i])
×
1392
    end
×
1393

1394
    kd0, ld0 = get_chunks_id(pos_d)
×
1395
    kd1, ld1 = get_chunks_id(pos_d + numbits - 1)
×
1396

1397
    delta_kd = kd1 - kd0
×
1398

1399
    u = _msk64
×
1400
    if delta_kd == 0
×
1401
        msk_d0 = msk_d1 = ~(u << ld0) | (u << (ld1+1))
×
1402
        lt0 = ld1
×
1403
    else
1404
        msk_d0 = ~(u << ld0)
×
1405
        msk_d1 = (u << (ld1+1))
×
1406
        lt0 = 63
×
1407
    end
1408

1409
    bind = kd0
×
1410
    ind = pos_s
×
1411
    @inbounds if ld0 > 0
×
1412
        c = UInt64(0)
×
1413
        for j = ld0:lt0
×
1414
            c |= (UInt64(unchecked_bool_convert(C[ind])) << j)
×
1415
            ind += 1
×
1416
        end
×
1417
        Bc[kd0] = (Bc[kd0] & msk_d0) | (c & ~msk_d0)
×
1418
        bind += 1
×
1419
    end
1420

1421
    nc = _div64(numbits - ind + pos_s)
×
1422
    @inbounds for i = 1:nc
×
1423
        c = UInt64(0)
×
1424
        for j = 0:63
×
1425
            c |= (UInt64(unchecked_bool_convert(C[ind])) << j)
×
1426
            ind += 1
×
1427
        end
×
1428
        Bc[bind] = c
×
1429
        bind += 1
×
1430
    end
×
1431

1432
    @inbounds if bind ≤ kd1
×
1433
        @assert bind == kd1
×
1434
        c = UInt64(0)
×
1435
        for j = 0:ld1
×
1436
            c |= (UInt64(unchecked_bool_convert(C[ind])) << j)
×
1437
            ind += 1
×
1438
        end
×
1439
        Bc[kd1] = (Bc[kd1] & msk_d1) | (c & ~msk_d1)
×
1440
    end
1441
end
1442

1443
# contiguous multidimensional indexing: if the first dimension is a range,
1444
# we can get some performance from using copy_chunks!
1445

1446
@inline function setindex!(B::BitArray, X::Union{StridedArray,BitArray}, J0::Union{Colon,AbstractUnitRange{Int}})
×
1447
    I0 = to_indices(B, (J0,))[1]
×
1448
    @boundscheck checkbounds(B, I0)
×
1449
    l0 = length(I0)
×
1450
    setindex_shape_check(X, l0)
×
1451
    l0 == 0 && return B
×
1452
    f0 = indexoffset(I0)+1
×
1453
    copy_to_bitarray_chunks!(B.chunks, f0, X, 1, l0)
×
1454
    return B
×
1455
end
1456

1457
@inline function setindex!(B::BitArray, X::Union{StridedArray,BitArray},
2✔
1458
        I0::Union{Colon,AbstractUnitRange{Int}}, I::Union{Int,AbstractUnitRange{Int},Colon}...)
1459
    J = to_indices(B, (I0, I...))
2✔
1460
    @boundscheck checkbounds(B, J...)
2✔
1461
    _unsafe_setindex!(B, X, J...)
4✔
1462
end
1463
@generated function _unsafe_setindex!(B::BitArray, X::Union{StridedArray,BitArray},
6✔
1464
        I0::Union{Slice,AbstractUnitRange{Int}}, I::Union{Int,AbstractUnitRange{Int},Slice}...)
1465
    N = length(I)
1✔
1466
    quote
1✔
1467
        idxlens = @ncall $N index_lengths I0 d->I[d]
2✔
1468
        @ncall $N setindex_shape_check X idxlens[1] d->idxlens[d+1]
4✔
1469
        isempty(X) && return B
2✔
1470
        f0 = indexoffset(I0)+1
2✔
1471
        l0 = idxlens[1]
2✔
1472

1473
        gap_lst_1 = 0
2✔
1474
        @nexprs $N d->(gap_lst_{d+1} = idxlens[d+1])
2✔
1475
        stride = 1
2✔
1476
        ind = f0
2✔
1477
        @nexprs $N d->begin
×
1478
            stride *= size(B, d)
2✔
1479
            stride_lst_d = stride
2✔
1480
            ind += stride * indexoffset(I[d])
2✔
1481
            gap_lst_{d+1} *= stride
2✔
1482
        end
1483

1484
        refind = 1
2✔
1485
        Bc = B.chunks
2✔
1486
        @nloops($N, i, d->I[d],
4✔
1487
                d->nothing, # PRE
1488
                d->(ind += stride_lst_d - gap_lst_d), # POST
1489
                begin # BODY
1490
                    copy_to_bitarray_chunks!(Bc, ind, X, refind, l0)
4✔
1491
                    refind += l0
4✔
1492
                end)
1493

1494
        return B
2✔
1495
    end
1496
end
1497

1498
@propagate_inbounds function setindex!(B::BitArray, X::AbstractArray,
×
1499
        I0::Union{Colon,AbstractUnitRange{Int}}, I::Union{Int,AbstractUnitRange{Int},Colon}...)
1500
    _setindex!(IndexStyle(B), B, X, to_indices(B, (I0, I...))...)
×
1501
end
1502

1503
## fill! contiguous views of BitArrays with a single value
1504
function fill!(V::SubArray{Bool, <:Any, <:BitArray, <:Tuple{AbstractUnitRange{Int}}}, x)
1,657✔
1505
    B = V.parent
1,661✔
1506
    I0 = V.indices[1]
1,661✔
1507
    l0 = length(I0)
1,719✔
1508
    l0 == 0 && return V
1,719✔
1509
    fill_chunks!(B.chunks, Bool(x), first(I0), l0)
1,723✔
1510
    return V
1,719✔
1511
end
1512

1513
fill!(V::SubArray{Bool, <:Any, <:BitArray, <:Tuple{AbstractUnitRange{Int}, Vararg{Union{Int,AbstractUnitRange{Int}}}}}, x) =
×
1514
    _unsafe_fill_indices!(V.parent, x, V.indices...)
1515

1516
@generated function _unsafe_fill_indices!(B::BitArray, x,
×
1517
        I0::AbstractUnitRange{Int}, I::Union{Int,AbstractUnitRange{Int}}...)
1518
    N = length(I)
×
1519
    quote
×
1520
        y = Bool(x)
×
1521
        idxlens = @ncall $N index_lengths I0 d->I[d]
×
1522

1523
        f0 = indexoffset(I0)+1
×
1524
        l0 = idxlens[1]
×
1525
        l0 == 0 && return B
×
1526
        @nexprs $N d->(isempty(I[d]) && return B)
×
1527

1528
        gap_lst_1 = 0
×
1529
        @nexprs $N d->(gap_lst_{d+1} = idxlens[d+1])
×
1530
        stride = 1
×
1531
        ind = f0
×
1532
        @nexprs $N d->begin
×
1533
            stride *= size(B, d)
×
1534
            stride_lst_d = stride
×
1535
            ind += stride * indexoffset(I[d])
×
1536
            gap_lst_{d+1} *= stride
×
1537
        end
1538

1539
        @nloops($N, i, d->I[d],
×
1540
                d->nothing, # PRE
×
1541
                d->(ind += stride_lst_d - gap_lst_d), # POST
×
1542
                fill_chunks!(B.chunks, y, ind, l0) # BODY
1543
                )
1544

1545
        return B
×
1546
    end
1547
end
1548

1549
## isassigned
1550

1551
@generated function isassigned(B::BitArray, I_0::Int, I::Int...)
×
1552
    N = length(I)
1553
    quote
1554
        @nexprs $N d->(I_d = I[d])
×
1555
        stride = 1
×
1556
        index = I_0
×
1557
        @nexprs $N d->begin
×
1558
            l = size(B,d)
×
1559
            stride *= l
×
1560
            @boundscheck 1 <= I_{d-1} <= l || return false
×
1561
            index += (I_d - 1) * stride
×
1562
        end
1563
        return isassigned(B, index)
×
1564
    end
1565
end
1566

1567
isassigned(a::AbstractArray, i::CartesianIndex) = isassigned(a, Tuple(i)...)
6✔
1568
isassigned(a::AbstractArray, i::Union{Integer, CartesianIndex}...) = isassigned(a, CartesianIndex(i))
3✔
1569

1570
## permutedims
1571

1572
## Permute array dims ##
1573

1574
function permutedims(B::StridedArray, perm)
255✔
1575
    dimsB = size(B)
255✔
1576
    ndimsB = length(dimsB)
255✔
1577
    (ndimsB == length(perm) && isperm(perm)) || throw(ArgumentError("no valid permutation of dimensions"))
255✔
1578
    dimsP = ntuple(i->dimsB[perm[i]], ndimsB)::typeof(dimsB)
988✔
1579
    P = similar(B, dimsP)
255✔
1580
    permutedims!(P, B, perm)
255✔
1581
end
1582

1583
function checkdims_perm(P::AbstractArray{TP,N}, B::AbstractArray{TB,N}, perm) where {TP,TB,N}
322✔
1584
    indsB = axes(B)
322✔
1585
    length(perm) == N || throw(ArgumentError("expected permutation of size $N, but length(perm)=$(length(perm))"))
322✔
1586
    isperm(perm) || throw(ArgumentError("input is not a permutation"))
324✔
1587
    indsP = axes(P)
320✔
1588
    for i = 1:length(perm)
421✔
1589
        indsP[i] == indsB[perm[i]] || throw(DimensionMismatch("destination tensor of incorrect size"))
869✔
1590
    end
1,418✔
1591
    nothing
320✔
1592
end
1593

1594
for (V, PT, BT) in Any[((:N,), BitArray, BitArray), ((:T,:N), Array, StridedArray)]
1595
    @eval @generated function permutedims!(P::$PT{$(V...)}, B::$BT{$(V...)}, perm) where $(V...)
258✔
1596
        quote
61✔
1597
            checkdims_perm(P, B, perm)
258✔
1598

1599
            #calculates all the strides
1600
            native_strides = size_to_strides(1, size(B)...)
258✔
1601
            strides_1 = 0
258✔
1602
            @nexprs $N d->(strides_{d+1} = native_strides[perm[d]])
258✔
1603

1604
            #Creates offset, because indexing starts at 1
1605
            offset = 1 - sum(@ntuple $N d->strides_{d+1})
258✔
1606

1607
            sumc = 0
258✔
1608
            ind = 1
258✔
1609
            @nloops($N, i, P,
2,031✔
1610
                    d->(sumc += i_d*strides_{d+1}), # PRE
1611
                    d->(sumc -= i_d*strides_{d+1}), # POST
1612
                    begin # BODY
1613
                        @inbounds P[ind] = B[sumc+offset]
268,413✔
1614
                        ind += 1
268,413✔
1615
                    end)
1616

1617
            return P
258✔
1618
        end
1619
    end
1620
end
1621

1622
## unique across dim
1623

1624
# TODO: this doesn't fit into the new hashing scheme in any obvious way
1625

1626
struct Prehashed
1627
    hash::UInt
893✔
1628
end
1629
hash(x::Prehashed) = x.hash
893✔
1630

1631
"""
1632
    unique(A::AbstractArray; dims::Int)
1633

1634
Return unique regions of `A` along dimension `dims`.
1635

1636
# Examples
1637
```jldoctest
1638
julia> A = map(isodd, reshape(Vector(1:8), (2,2,2)))
1639
2×2×2 Array{Bool, 3}:
1640
[:, :, 1] =
1641
 1  1
1642
 0  0
1643

1644
[:, :, 2] =
1645
 1  1
1646
 0  0
1647

1648
julia> unique(A)
1649
2-element Vector{Bool}:
1650
 1
1651
 0
1652

1653
julia> unique(A, dims=2)
1654
2×1×2 Array{Bool, 3}:
1655
[:, :, 1] =
1656
 1
1657
 0
1658

1659
[:, :, 2] =
1660
 1
1661
 0
1662

1663
julia> unique(A, dims=3)
1664
2×2×1 Array{Bool, 3}:
1665
[:, :, 1] =
1666
 1  1
1667
 0  0
1668
```
1669
"""
1670
unique(A::AbstractArray; dims::Union{Colon,Integer} = :) = _unique_dims(A, dims)
16,838✔
1671

1672
_unique_dims(A::AbstractArray, dims::Colon) = invoke(unique, Tuple{Any}, A)
8,408✔
1673

1674
@generated function _unique_dims(A::AbstractArray{T,N}, dim::Integer) where {T,N}
22✔
1675
    quote
5✔
1676
        1 <= dim <= $N || return copy(A)
11✔
1677
        hashes = zeros(UInt, axes(A, dim))
443✔
1678

1679
        # Compute hash for each row
1680
        k = 0
11✔
1681
        @nloops $N i A d->(if d == dim; k = i_d; end) begin
4,416✔
1682
            @inbounds hashes[k] = hash(hashes[k], hash((@nref $N A i)))
8,241✔
1683
        end
1684

1685
        # Collect index of first row for each hash
1686
        uniquerow = similar(Array{Int}, axes(A, dim))
13✔
1687
        firstrow = Dict{Prehashed,Int}()
11✔
1688
        for k = axes(A, dim)
22✔
1689
            uniquerow[k] = get!(firstrow, Prehashed(hashes[k]), k)
443✔
1690
        end
875✔
1691
        uniquerows = collect(values(firstrow))
11✔
1692

1693
        # Check for collisions
1694
        collided = falses(axes(A, dim))
11✔
1695
        @inbounds begin
11✔
1696
            @nloops $N i A d->(if d == dim
165✔
1697
                k = i_d
4,251✔
1698
                j_d = uniquerow[k]
4,251✔
1699
            else
1700
                j_d = i_d
4,195✔
1701
            end) begin
1702
                if !isequal((@nref $N A j), (@nref $N A i))
8,241✔
1703
                    collided[k] = true
170✔
1704
                end
1705
            end
1706
        end
1707

1708
        if any(collided)
28✔
1709
            nowcollided = similar(BitArray, axes(A, dim))
1✔
1710
            while any(collided)
20✔
1711
                # Collect index of first row for each collided hash
1712
                empty!(firstrow)
9✔
1713
                for j = axes(A, dim)
18✔
1714
                    collided[j] || continue
900✔
1715
                    uniquerow[j] = get!(firstrow, Prehashed(hashes[j]), j)
450✔
1716
                end
1,791✔
1717
                for v in values(firstrow)
18✔
1718
                    push!(uniquerows, v)
9✔
1719
                end
18✔
1720

1721
                # Check for collisions
1722
                fill!(nowcollided, false)
9✔
1723
                @nloops $N i A d->begin
90✔
1724
                    if d == dim
9,090✔
1725
                        k = i_d
9,000✔
1726
                        j_d = uniquerow[k]
9,000✔
1727
                        (!collided[k] || j_d == k) && continue
13,500✔
1728
                    else
1729
                        j_d = i_d
90✔
1730
                    end
1731
                end begin
1732
                    if !isequal((@nref $N A j), (@nref $N A i))
4,410✔
1733
                        nowcollided[k] = true
640✔
1734
                    end
1735
                end
1736
                (collided, nowcollided) = (nowcollided, collided)
9✔
1737
            end
9✔
1738
        end
1739

1740
        @nref $N A d->d == dim ? sort!(uniquerows) : (axes(A, d))
11✔
1741
    end
1742
end
1743

1744
# Show for pairs() with Cartesian indices. Needs to be here rather than show.jl for bootstrap order
1745
function Base.showarg(io::IO, r::Iterators.Pairs{<:Integer, <:Any, <:Any, T}, toplevel) where T <: Union{AbstractVector, Tuple}
1✔
1746
    print(io, "pairs(::$T)")
1✔
1747
end
1748
function Base.showarg(io::IO, r::Iterators.Pairs{<:CartesianIndex, <:Any, <:Any, T}, toplevel) where T <: AbstractArray
×
1749
    print(io, "pairs(::$T)")
×
1750
end
1751

1752
function Base.showarg(io::IO, r::Iterators.Pairs{<:CartesianIndex, <:Any, <:Any, T}, toplevel) where T<:AbstractVector
1✔
1753
    print(io, "pairs(IndexCartesian(), ::$T)")
1✔
1754
end
1755

1756
## sortslices
1757

1758
"""
1759
    sortslices(A; dims, alg::Algorithm=DEFAULT_UNSTABLE, lt=isless, by=identity, rev::Bool=false, order::Ordering=Forward)
1760

1761
Sort slices of an array `A`. The required keyword argument `dims` must
1762
be either an integer or a tuple of integers. It specifies the
1763
dimension(s) over which the slices are sorted.
1764

1765
E.g., if `A` is a matrix, `dims=1` will sort rows, `dims=2` will sort columns.
1766
Note that the default comparison function on one dimensional slices sorts
1767
lexicographically.
1768

1769
For the remaining keyword arguments, see the documentation of [`sort!`](@ref).
1770

1771
# Examples
1772
```jldoctest
1773
julia> sortslices([7 3 5; -1 6 4; 9 -2 8], dims=1) # Sort rows
1774
3×3 Matrix{Int64}:
1775
 -1   6  4
1776
  7   3  5
1777
  9  -2  8
1778

1779
julia> sortslices([7 3 5; -1 6 4; 9 -2 8], dims=1, lt=(x,y)->isless(x[2],y[2]))
1780
3×3 Matrix{Int64}:
1781
  9  -2  8
1782
  7   3  5
1783
 -1   6  4
1784

1785
julia> sortslices([7 3 5; -1 6 4; 9 -2 8], dims=1, rev=true)
1786
3×3 Matrix{Int64}:
1787
  9  -2  8
1788
  7   3  5
1789
 -1   6  4
1790

1791
julia> sortslices([7 3 5; 6 -1 -4; 9 -2 8], dims=2) # Sort columns
1792
3×3 Matrix{Int64}:
1793
  3   5  7
1794
 -1  -4  6
1795
 -2   8  9
1796

1797
julia> sortslices([7 3 5; 6 -1 -4; 9 -2 8], dims=2, alg=InsertionSort, lt=(x,y)->isless(x[2],y[2]))
1798
3×3 Matrix{Int64}:
1799
  5   3  7
1800
 -4  -1  6
1801
  8  -2  9
1802

1803
julia> sortslices([7 3 5; 6 -1 -4; 9 -2 8], dims=2, rev=true)
1804
3×3 Matrix{Int64}:
1805
 7   5   3
1806
 6  -4  -1
1807
 9   8  -2
1808
```
1809

1810
# Higher dimensions
1811

1812
`sortslices` extends naturally to higher dimensions. E.g., if `A` is a
1813
a 2x2x2 array, `sortslices(A, dims=3)` will sort slices within the 3rd dimension,
1814
passing the 2x2 slices `A[:, :, 1]` and `A[:, :, 2]` to the comparison function.
1815
Note that while there is no default order on higher-dimensional slices, you may
1816
use the `by` or `lt` keyword argument to specify such an order.
1817

1818
If `dims` is a tuple, the order of the dimensions in `dims` is
1819
relevant and specifies the linear order of the slices. E.g., if `A` is three
1820
dimensional and `dims` is `(1, 2)`, the orderings of the first two dimensions
1821
are re-arranged such that the slices (of the remaining third dimension) are sorted.
1822
If `dims` is `(2, 1)` instead, the same slices will be taken,
1823
but the result order will be row-major instead.
1824

1825
# Higher dimensional examples
1826
```
1827
julia> A = permutedims(reshape([4 3; 2 1; 'A' 'B'; 'C' 'D'], (2, 2, 2)), (1, 3, 2))
1828
2×2×2 Array{Any, 3}:
1829
[:, :, 1] =
1830
 4  3
1831
 2  1
1832

1833
[:, :, 2] =
1834
 'A'  'B'
1835
 'C'  'D'
1836

1837
julia> sortslices(A, dims=(1,2))
1838
2×2×2 Array{Any, 3}:
1839
[:, :, 1] =
1840
 1  3
1841
 2  4
1842

1843
[:, :, 2] =
1844
 'D'  'B'
1845
 'C'  'A'
1846

1847
julia> sortslices(A, dims=(2,1))
1848
2×2×2 Array{Any, 3}:
1849
[:, :, 1] =
1850
 1  2
1851
 3  4
1852

1853
[:, :, 2] =
1854
 'D'  'C'
1855
 'B'  'A'
1856

1857
julia> sortslices(reshape([5; 4; 3; 2; 1], (1,1,5)), dims=3, by=x->x[1,1])
1858
1×1×5 Array{Int64, 3}:
1859
[:, :, 1] =
1860
 1
1861

1862
[:, :, 2] =
1863
 2
1864

1865
[:, :, 3] =
1866
 3
1867

1868
[:, :, 4] =
1869
 4
1870

1871
[:, :, 5] =
1872
 5
1873
```
1874
"""
1875
function sortslices(A::AbstractArray; dims::Union{Integer, Tuple{Vararg{Integer}}}, kws...)
28✔
1876
    _sortslices(A, Val{dims}(); kws...)
14✔
1877
end
1878

1879
# Works around inference's lack of ability to recognize partial constness
1880
struct DimSelector{dims, T}
1881
    A::T
14✔
1882
end
1883
DimSelector{dims}(x::T) where {dims, T} = DimSelector{dims, T}(x)
14✔
1884
(ds::DimSelector{dims, T})(i) where {dims, T} = i in dims ? axes(ds.A, i) : (:,)
36✔
1885

1886
_negdims(n, dims) = filter(i->!(i in dims), 1:n)
58✔
1887

1888
function compute_itspace(A, ::Val{dims}) where {dims}
14✔
1889
    negdims = _negdims(ndims(A), dims)
14✔
1890
    axs = Iterators.product(ntuple(DimSelector{dims}(A), ndims(A))...)
18✔
1891
    vec(permutedims(collect(axs), (dims..., negdims...)))
14✔
1892
end
1893

1894
function _sortslices(A::AbstractArray, d::Val{dims}; kws...) where dims
28✔
1895
    itspace = compute_itspace(A, d)
14✔
1896
    vecs = map(its->view(A, its...), itspace)
78✔
1897
    p = sortperm(vecs; kws...)
22✔
1898
    if ndims(A) == 2 && isa(dims, Integer) && isa(A, Array)
14✔
1899
        # At the moment, the performance of the generic version is subpar
1900
        # (about 5x slower). Hardcode a fast-path until we're able to
1901
        # optimize this.
1902
        return dims == 1 ? A[p, :] : A[:, p]
8✔
1903
    else
1904
        B = similar(A)
8✔
1905
        for (x, its) in zip(p, itspace)
8✔
1906
            B[its...] = vecs[x]
24✔
1907
        end
30✔
1908
        B
6✔
1909
    end
1910
end
1911

1912
getindex(b::Ref, ::CartesianIndex{0}) = getindex(b)
4✔
1913
setindex!(b::Ref, x, ::CartesianIndex{0}) = setindex!(b, x)
2✔
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

© 2025 Coveralls, Inc