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

JuliaLang / julia / #37632

26 Sep 2023 06:44AM UTC coverage: 86.999% (-0.9%) from 87.914%
#37632

push

local

web-flow
inference: make `throw` block deoptimization concrete-eval friendly (#49235)

The deoptimization can sometimes destroy the effects analysis and
disable [semi-]concrete evaluation that is otherwise possible. This is
because the deoptimization was designed with the type domain
profitability in mind (#35982), and hasn't been adequately considering
the effects domain.

This commit makes the deoptimization aware of the effects domain more
and enables the `throw` block deoptimization only when the effects
already known to be ineligible for concrete-evaluation.

In our current effect system, `ALWAYS_FALSE`/`false` means that the
effect can not be refined to `ALWAYS_TRUE`/`true` anymore (unless given
user annotation later). Therefore we can enable the `throw` block
deoptimization without hindering the chance of concrete-evaluation when
any of the following conditions are met:
- `effects.consistent === ALWAYS_FALSE`
- `effects.effect_free === ALWAYS_FALSE`
- `effects.terminates === false`
- `effects.nonoverlayed === false`

Here are some numbers:

| Metric | master | this commit | #35982 reverted (set
`unoptimize_throw_blocks=false`) |

|-------------------------|-----------|-------------|--------------------------------------------|
| Base (seconds) | 15.579300 | 15.206645 | 15.296319 |
| Stdlibs (seconds) | 17.919013 | 17.667094 | 17.738128 |
| Total (seconds) | 33.499279 | 32.874737 | 33.035448 |
| Precompilation (seconds) | 49.967516 | 49.421121 | 49.999998 |
| First time `plot(rand(10,3))` [^1] | `2.476678 seconds (11.74 M
allocations)` | `2.430355 seconds (11.77 M allocations)` | `2.514874
seconds (11.64 M allocations)` |
| First time `solve(prob, QNDF())(5.0)` [^2] | `4.469492 seconds (15.32
M allocations)` | `4.499217 seconds (15.41 M allocations)` | `4.470772
seconds (15.38 M allocations)` |

[^1]: With disabling precompilation of Plots.jl.
[^2]: With disabling precompilation of OrdinaryDiffEq.

These numbers ma... (continued)

7 of 7 new or added lines in 1 file covered. (100.0%)

73407 of 84377 relevant lines covered (87.0%)

11275130.05 hits per line

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

91.01
/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!,
6
                  min, max, zero, oneunit, isless, eachindex,
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)
302,102,817✔
76
    end
77

78
    CartesianIndex(index::NTuple{N,Integer}) where {N} = CartesianIndex{N}(index)
302,103,216✔
79
    CartesianIndex(index::Integer...) = CartesianIndex(index)
186,988,486✔
80
    CartesianIndex{N}(index::Vararg{Integer,N}) where {N} = CartesianIndex{N}(index)
200✔
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))
38,235,232✔
87
    flatten(::Tuple{}) = ()
×
88
    flatten(I::Tuple{Any}) = Tuple(I[1])
38,235,232✔
89
    @inline flatten(I::Tuple) = (Tuple(I[1])..., flatten(tail(I))...)
38,235,237✔
90
    CartesianIndex(index::Tuple{Vararg{Union{Integer, CartesianIndex}}}) = CartesianIndex(index...)
11✔
91
    show(io::IO, i::CartesianIndex) = (print(io, "CartesianIndex"); show(io, i.I))
×
92

93
    # length
94
    length(::CartesianIndex{N}) where {N} = N
208,936,978✔
95
    length(::Type{CartesianIndex{N}}) where {N} = N
×
96

97
    # indexing
98
    getindex(index::CartesianIndex, i::Integer) = index.I[i]
49,717✔
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
38,339,160✔
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,502,857✔
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} =
467✔
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}
606✔
150
        iter = CartesianIndices(axes(a))
606✔
151
        # might overflow
152
        I = inc(i.I, iter.indices)
878✔
153
        return I
606✔
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)
99✔
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
565,715✔
255
    end
256

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

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

267
    CartesianIndices(A::AbstractArray) = CartesianIndices(axes(A))
71,052✔
268

269
    _convert2ind(sz::Bool) = Base.OneTo(Int8(sz))
6✔
270
    _convert2ind(sz::Integer) = Base.oneto(sz)
911✔
271
    _convert2ind(sz::AbstractUnitRange) = first(sz):last(sz)
10✔
272
    _convert2ind(sz::OrdinalRange) = first(sz):step(sz):last(sz)
34✔
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)))
46✔
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 =
343✔
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,353,704✔
345
    Base.has_offset_axes(iter::CartesianIndices) = Base.has_offset_axes(iter.indices...)
1✔
346
    @propagate_inbounds function isassigned(iter::CartesianIndices{N,R}, I::Vararg{Int, N}) where {N,R}
×
347
        for i in 1:N
×
348
            isassigned(iter.indices[i], I[i]) || return false
×
349
        end
×
350
        return true
×
351
    end
352

353
    # getindex for a 0D CartesianIndices is necessary for disambiguation
354
    @propagate_inbounds function Base.getindex(iter::CartesianIndices{0,R}) where {R}
3✔
355
        CartesianIndex()
3✔
356
    end
357
    @inline function Base.getindex(iter::CartesianIndices{N,R}, I::Vararg{Int, N}) where {N,R}
608,356✔
358
        # Eagerly do boundscheck before calculating each item of the CartesianIndex so that
359
        # we can pass `@inbounds` hint to inside the map and generates more efficient SIMD codes (#42115)
360
        @boundscheck checkbounds(iter, I...)
608,356✔
361
        index = map(iter.indices, I) do r, i
608,356✔
362
            @inbounds getindex(r, i)
1,643,781✔
363
        end
364
        CartesianIndex(index)
608,356✔
365
    end
366

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

384
    # If dimensions permit, we may index into a CartesianIndices directly instead of constructing a SubArray wrapper
385
    @propagate_inbounds function Base.view(c::CartesianIndices{N}, r::Vararg{Union{OrdinalRange{<:Integer, <:Integer}, Colon},N}) where {N}
2,002✔
386
        getindex(c, r...)
2,002✔
387
    end
388
    @propagate_inbounds function Base.view(c::CartesianIndices{N}, C::CartesianIndices{N}) where {N}
×
389
        getindex(c, C)
×
390
    end
391

392
    eachindex(::IndexCartesian, A::AbstractArray) = CartesianIndices(axes(A))
58,766✔
393

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

400
    @inline function iterate(iter::CartesianIndices)
206,058✔
401
        iterfirst = first(iter)
204,567✔
402
        if !all(map(in, iterfirst.I, iter.indices))
207,182✔
403
            return nothing
177✔
404
        end
405
        iterfirst, iterfirst
206,345✔
406
    end
407
    @inline function iterate(iter::CartesianIndices, state)
92,983,663✔
408
        valid, I = __inc(state.I, iter.indices)
93,725,276✔
409
        valid || return nothing
93,187,687✔
410
        return CartesianIndex(I...), CartesianIndex(I...)
92,784,539✔
411
    end
412

413
    # increment & carry
414
    @inline function inc(state, indices)
1,184,726✔
415
        _, I = __inc(state, indices)
1,246,230✔
416
        return CartesianIndex(I...)
1,184,726✔
417
    end
418

419
    # Unlike ordinary ranges, CartesianIndices continues the iteration in the next column when the
420
    # current column is consumed. The implementation is written recursively to achieve this.
421
    # `iterate` returns `Union{Nothing, Tuple}`, we explicitly pass a `valid` flag to eliminate
422
    # the type instability inside the core `__inc` logic, and this gives better runtime performance.
423
    __inc(::Tuple{}, ::Tuple{}) = false, ()
2✔
424
    @inline function __inc(state::Tuple{Int}, indices::Tuple{OrdinalRangeInt})
1,466,076✔
425
        rng = indices[1]
1,466,003✔
426
        I = state[1] + step(rng)
1,472,980✔
427
        valid = __is_valid_range(I, rng) && state[1] != last(rng)
1,472,987✔
428
        return valid, (I, )
1,472,980✔
429
    end
430
    @inline function __inc(state::Tuple{Int,Int,Vararg{Int}}, indices::Tuple{OrdinalRangeInt,OrdinalRangeInt,Vararg{OrdinalRangeInt}})
93,539,086✔
431
        rng = indices[1]
93,539,086✔
432
        I = state[1] + step(rng)
93,539,086✔
433
        if __is_valid_range(I, rng) && state[1] != last(rng)
93,539,121✔
434
            return true, (I, tail(state)...)
92,697,864✔
435
        end
436
        valid, I = __inc(tail(state), tail(indices))
881,776✔
437
        return valid, (first(rng), I...)
841,222✔
438
    end
439

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

450
    # 0-d cartesian ranges are special-cased to iterate once and only once
451
    iterate(iter::CartesianIndices{0}, done=false) = done ? nothing : (CartesianIndex(), true)
9,724✔
452

453
    size(iter::CartesianIndices) = map(length, iter.indices)
129,436✔
454

455
    length(iter::CartesianIndices) = prod(size(iter))
128,041✔
456

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

460
    first(iter::CartesianIndices) = CartesianIndex(map(first, iter.indices))
318,883✔
461
    last(iter::CartesianIndices)  = CartesianIndex(map(last, iter.indices))
13,311✔
462

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

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

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

473
    simd_outer_range(iter::CartesianIndices{0}) = iter
1✔
474
    function simd_outer_range(iter::CartesianIndices)
138,894✔
475
        CartesianIndices(tail(iter.indices))
138,894✔
476
    end
477

478
    simd_inner_length(iter::CartesianIndices{0}, ::CartesianIndex) = 1
1✔
479
    simd_inner_length(iter::CartesianIndices, I::CartesianIndex) = Base.length(iter.indices[1])
922,975✔
480

481
    simd_index(iter::CartesianIndices{0}, ::CartesianIndex, I1::Int) = first(iter)
1✔
482
    @propagate_inbounds simd_index(iter::CartesianIndices, Ilast::CartesianIndex, I1::Int) =
38,235,221✔
483
        CartesianIndex(iter.indices[1][I1+firstindex(iter.indices[1])], Ilast)
484

485
    # Split out the first N elements of a tuple
486
    @inline function split(t, V::Val)
208,944,264✔
487
        ref = ntuple(Returns(true), V)  # create a reference tuple of length N
208,944,264✔
488
        _split1(t, ref), _splitrest(t, ref)
208,944,264✔
489
    end
490
    @inline _split1(t, ref) = (t[1], _split1(tail(t), tail(ref))...)
10,628✔
491
    @inline _splitrest(t, ref) = _splitrest(tail(t), tail(ref))
10,628✔
492
    # exit either when we've exhausted the input or reference tuple
493
    _split1(::Tuple{}, ::Tuple{}) = ()
896,140✔
494
    _split1(::Tuple{}, ref) = ()
208,045,943✔
495
    _split1(t, ::Tuple{}) = ()
2,181✔
496
    _splitrest(::Tuple{}, ::Tuple{}) = ()
896,140✔
497
    _splitrest(t, ::Tuple{}) = t
2,181✔
498
    _splitrest(::Tuple{}, ref) = ()
208,045,943✔
499

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

676

677
using .IteratorsMD
678

679
## Bounds-checking with CartesianIndex
680
# Disallow linear indexing with CartesianIndex
681
function checkbounds(::Type{Bool}, A::AbstractArray, i::Union{CartesianIndex, AbstractArray{<:CartesianIndex}})
3,406✔
682
    @inline
3,406✔
683
    checkbounds_indices(Bool, axes(A), (i,))
5,023✔
684
end
685

686
@inline checkbounds_indices(::Type{Bool}, ::Tuple{}, I::Tuple{CartesianIndex,Vararg{Any}}) =
×
687
    checkbounds_indices(Bool, (), (I[1].I..., tail(I)...))
688
@inline checkbounds_indices(::Type{Bool}, IA::Tuple{Any}, I::Tuple{CartesianIndex,Vararg{Any}}) =
1,033✔
689
    checkbounds_indices(Bool, IA, (I[1].I..., tail(I)...))
690
@inline checkbounds_indices(::Type{Bool}, IA::Tuple, I::Tuple{CartesianIndex,Vararg{Any}}) =
37,604,495✔
691
    checkbounds_indices(Bool, IA, (I[1].I..., tail(I)...))
692

693
# Indexing into Array with mixtures of Integers and CartesianIndices is
694
# extremely performance-sensitive. While the abstract fallbacks support this,
695
# codegen has extra support for SIMDification that sub2ind doesn't (yet) support
696
@propagate_inbounds getindex(A::Array, i1::Union{Integer, CartesianIndex}, I::Union{Integer, CartesianIndex}...) =
74,135,905✔
697
    A[to_indices(A, (i1, I...))...]
698
@propagate_inbounds setindex!(A::Array, v, i1::Union{Integer, CartesianIndex}, I::Union{Integer, CartesianIndex}...) =
69,332,446✔
699
    (A[to_indices(A, (i1, I...))...] = v; A)
41,381,075✔
700

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

722

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

732
function checkindex(::Type{Bool}, inds::Tuple, I::AbstractArray{<:CartesianIndex})
1,712✔
733
    b = true
1,712✔
734
    for i in I
1,714✔
735
        b &= checkbounds_indices(Bool, inds, (i,))
6,843✔
736
    end
8,575✔
737
    b
1,712✔
738
end
739
checkindex(::Type{Bool}, inds::Tuple, I::CartesianIndices) = all(checkindex.(Bool, inds, I.indices))
35✔
740

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

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

763
# Recursively compute the lengths of a list of indices, without dropping scalars
764
index_lengths() = ()
110,192✔
765
@inline index_lengths(::Real, rest...) = (1, index_lengths(rest...)...)
29,455✔
766
@inline index_lengths(A::AbstractArray, rest...) = (length(A), index_lengths(rest...)...)
117,982✔
767

768
# shape of array to create for getindex() with indices I, dropping scalars
769
# returns a Tuple{Vararg{AbstractUnitRange}} of indices
770
index_shape() = ()
×
771
@inline index_shape(::Real, rest...) = index_shape(rest...)
15,479✔
772
@inline index_shape(A::AbstractArray, rest...) = (axes(A)..., index_shape(rest...)...)
217,670✔
773

774
"""
775
    LogicalIndex(mask)
776

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

847
@inline checkbounds(::Type{Bool}, A::AbstractArray, I::LogicalIndex{<:Any,<:AbstractArray{Bool,1}}) =
34,601✔
848
    eachindex(IndexLinear(), A) == eachindex(IndexLinear(), I.mask)
849
@inline checkbounds(::Type{Bool}, A::AbstractArray, I::LogicalIndex) = axes(A) == axes(I.mask)
77✔
850
@inline checkindex(::Type{Bool}, indx::AbstractUnitRange, I::LogicalIndex) = (indx,) == axes(I.mask)
2,270✔
851
checkindex(::Type{Bool}, inds::Tuple, I::LogicalIndex) = checkbounds_indices(Bool, inds, axes(I.mask))
28✔
852

853
ensure_indexable(I::Tuple{}) = ()
×
854
@inline ensure_indexable(I::Tuple{Any, Vararg{Any}}) = (I[1], ensure_indexable(tail(I))...)
4,443,200✔
855
@inline ensure_indexable(I::Tuple{LogicalIndex, Vararg{Any}}) = (collect(I[1]), ensure_indexable(tail(I))...)
576✔
856

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

874
# Colons get converted to slices by `uncolon`
875
_to_indices1(A, inds, I1::Colon) = (uncolon(inds),)
74,109✔
876

877
uncolon(::Tuple{}) = Slice(OneTo(1))
363✔
878
uncolon(inds::Tuple) = Slice(inds[1])
73,750✔
879

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

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

887
@inline function _getindex(l::IndexStyle, A::AbstractArray, I::Union{Real, AbstractArray}...)
143,833✔
888
    @boundscheck checkbounds(A, I...)
206,142✔
889
    return _unsafe_getindex(l, _maybe_reshape(l, A, I...), I...)
516,592✔
890
end
891
# But we can speed up IndexCartesian arrays by reshaping them to the appropriate dimensionality:
892
_maybe_reshape(::IndexLinear, A::AbstractArray, I...) = A
165,578✔
893
_maybe_reshape(::IndexCartesian, A::AbstractVector, I...) = A
56✔
894
@inline _maybe_reshape(::IndexCartesian, A::AbstractArray, I...) = __maybe_reshape(A, index_ndims(I...))
1,767✔
895
@inline __maybe_reshape(A::AbstractArray{T,N}, ::NTuple{N,Any}) where {T,N} = A
1,351✔
896
@inline __maybe_reshape(A::AbstractArray, ::NTuple{N,Any}) where {N} = reshape(A, Val(N))
416✔
897

898
function _unsafe_getindex(::IndexStyle, A::AbstractArray, I::Vararg{Union{Real, AbstractArray}, N}) where N
206,057✔
899
    # This is specifically not inlined to prevent excessive allocations in type unstable code
900
    shape = index_shape(I...)
206,057✔
901
    dest = similar(A, shape)
206,122✔
902
    map(length, axes(dest)) == map(length, shape) || throw_checksize_error(dest, shape)
206,057✔
903
    _unsafe_getindex!(dest, A, I...) # usually a generated function, don't allow it to impact inference result
4,329,877✔
904
    return dest
206,056✔
905
end
906

907
function _generate_unsafe_getindex!_body(N::Int)
220✔
908
    quote
220✔
909
        @inline
69,067✔
910
        D = eachindex(dest)
137,122✔
911
        Dy = iterate(D)
273,658✔
912
        @inbounds @nloops $N j d->I[d] begin
6,098,197✔
913
            # This condition is never hit, but at the moment
914
            # the optimizer is not clever enough to split the union without it
915
            Dy === nothing && return dest
4,210,666✔
916
            (idx, state) = Dy
2,680,717✔
917
            dest[idx] = @ncall $N getindex src j
4,279,708✔
918
            Dy = iterate(D, state)
8,283,218✔
919
        end
920
        return dest
137,121✔
921
    end
922
end
923

924
# Always index with the exactly indices provided.
925
@generated function _unsafe_getindex!(dest::AbstractArray, src::AbstractArray, I::Vararg{Union{Real, AbstractArray}, N}) where N
65,674✔
926
    _generate_unsafe_getindex!_body(N)
220✔
927
end
928

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

934
@eval function _unsafe_getindex!(dest::AbstractArray, src::AbstractArray, I::Vararg{Union{Real, AbstractArray},2})
17,305✔
935
    $(_generate_unsafe_getindex!_body(2))
17,305✔
936
end
937

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

940
## setindex! ##
941
function _setindex!(l::IndexStyle, A::AbstractArray, x, I::Union{Real, AbstractArray}...)
30,832✔
942
    @inline
30,832✔
943
    @boundscheck checkbounds(A, I...)
30,832✔
944
    _unsafe_setindex!(l, _maybe_reshape(l, A, I...), x, I...)
230,072✔
945
    A
30,825✔
946
end
947

948
function _generate_unsafe_setindex!_body(N::Int)
93✔
949
    quote
93✔
950
        x′ = unalias(A, x)
35,334✔
951
        @nexprs $N d->(I_d = unalias(A, I[d]))
31,546✔
952
        idxlens = @ncall $N index_lengths I
30,832✔
953
        @ncall $N setindex_shape_check x′ (d->idxlens[d])
33,502✔
954
        Xy = iterate(x′)
58,046✔
955
        @inbounds @nloops $N i d->I_d begin
266,219✔
956
            # This is never reached, but serves as an assumption for
957
            # the optimizer that it does not need to emit error paths
958
            Xy === nothing && break
759,104✔
959
            (val, state) = Xy
759,104✔
960
            @ncall $N setindex! A val i
759,172✔
961
            Xy = iterate(x′, state)
986,337✔
962
        end
963
        A
30,825✔
964
    end
965
end
966

967
@generated function _unsafe_setindex!(::IndexStyle, A::AbstractArray, x, I::Vararg{Union{Real,AbstractArray}, N}) where N
1,184✔
968
    _generate_unsafe_setindex!_body(N)
93✔
969
end
970

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

975
@eval function _unsafe_setindex!(::IndexStyle, A::AbstractArray, x, I::Vararg{Union{Real,AbstractArray},2})
29,551✔
976
    $(_generate_unsafe_setindex!_body(2))
29,551✔
977
end
978

979
diff(a::AbstractVector) = diff(a, dims=1)
20✔
980

981
"""
982
    diff(A::AbstractVector)
983
    diff(A::AbstractArray; dims::Integer)
984

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

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

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

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

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

1015
    r = axes(a)
42✔
1016
    r0 = ntuple(i -> i == dims ? UnitRange(1, last(r[i]) - 1) : UnitRange(r[i]), N)
109✔
1017
    r1 = ntuple(i -> i == dims ? UnitRange(2, last(r[i])) : UnitRange(r[i]), N)
109✔
1018

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

1026
### from abstractarray.jl
1027

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

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

1079
"""
1080
    fill!(A, x)
1081

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

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

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

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

1104
julia> x = 0; f() = (global x += 1; x); fill!(Vector{Int}(undef, 3), f())
1105
3-element Vector{Int64}:
1106
 1
1107
 1
1108
 1
1109
```
1110
"""
1111
function fill!(A::AbstractArray{T}, x) where T
150,084✔
1112
    xT = convert(T, x)
150,091✔
1113
    for I in eachindex(A)
236,601✔
1114
        @inbounds A[I] = xT
75,770,731✔
1115
    end
151,365,021✔
1116
    A
153,073✔
1117
end
1118

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

1147
"""
1148
    copyto!(dest, Rdest::CartesianIndices, src, Rsrc::CartesianIndices) -> dest
1149

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

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

1157
julia> B = [1 2; 3 4];
1158

1159
julia> Ainds = CartesianIndices((2:3, 2:3));
1160

1161
julia> Binds = CartesianIndices(B);
1162

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

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

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

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

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

1195
circshift!(dest::AbstractArray, src, shiftamt) =
3✔
1196
    circshift!(dest, src, map(Integer, (shiftamt...,)))
1197

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

1231
# circcopy!
1232
"""
1233
    circcopy!(dest, src)
1234

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

1241
See also: [`circshift`](@ref).
1242

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

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

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

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

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

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

1296
### BitArrays
1297

1298
## getindex
1299

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

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

1315
        idxlens = @ncall $N index_lengths I0 I
77,986✔
1316

1317
        f0 = indexoffset(I0)+1
77,672✔
1318
        l0 = idxlens[1]
77,568✔
1319

1320
        gap_lst_1 = 0
77,568✔
1321
        @nexprs $N d->(gap_lst_{d+1} = idxlens[d+1])
2,056✔
1322
        stride = 1
77,568✔
1323
        ind = f0
77,568✔
1324
        @nexprs $N d->begin
×
1325
            stride *= size(B, d)
4,025✔
1326
            stride_lst_d = stride
3,711✔
1327
            ind += stride * indexoffset(I_d)
4,025✔
1328
            gap_lst_{d+1} *= stride
3,711✔
1329
        end
1330

1331
        storeind = 1
77,568✔
1332
        Xc, Bc = X.chunks, B.chunks
77,986✔
1333
        @nloops($N, i, d->(1:idxlens[d+1]),
14,735✔
1334
                d->nothing, # PRE
1335
                d->(ind += stride_lst_d - gap_lst_d), # POST
1336
                begin # BODY
1337
                    copy_chunks!(Xc, storeind, Bc, ind, l0)
91,815✔
1338
                    storeind += l0
91,397✔
1339
                end)
1340
        return X
77,568✔
1341
    end
1342
end
1343

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

1364
## setindex!
1365

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

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

1387
function copy_to_bitarray_chunks!(Bc::Vector{UInt64}, pos_d::Int, C::StridedArray{<:Real}, pos_s::Int, numbits::Int)
23,816✔
1388
    @inbounds for i = (1:numbits) .+ (pos_s - 1)
47,632✔
1389
        try_bool_conversion(C[i])
8,309,148✔
1390
    end
9,468,760✔
1391

1392
    kd0, ld0 = get_chunks_id(pos_d)
23,816✔
1393
    kd1, ld1 = get_chunks_id(pos_d + numbits - 1)
23,816✔
1394

1395
    delta_kd = kd1 - kd0
23,816✔
1396

1397
    u = _msk64
23,816✔
1398
    if delta_kd == 0
23,816✔
1399
        msk_d0 = msk_d1 = ~(u << ld0) | (u << (ld1+1))
11,656✔
1400
        lt0 = ld1
11,656✔
1401
    else
1402
        msk_d0 = ~(u << ld0)
12,160✔
1403
        msk_d1 = (u << (ld1+1))
12,160✔
1404
        lt0 = 63
12,160✔
1405
    end
1406

1407
    bind = kd0
23,816✔
1408
    ind = pos_s
23,816✔
1409
    @inbounds if ld0 > 0
23,816✔
1410
        c = UInt64(0)
20,908✔
1411
        for j = ld0:lt0
41,816✔
1412
            c |= (UInt64(unchecked_bool_convert(C[ind])) << j)
128,636✔
1413
            ind += 1
128,636✔
1414
        end
236,364✔
1415
        Bc[kd0] = (Bc[kd0] & msk_d0) | (c & ~msk_d0)
20,908✔
1416
        bind += 1
20,908✔
1417
    end
1418

1419
    nc = _div64(numbits - ind + pos_s)
23,816✔
1420
    @inbounds for i = 1:nc
32,884✔
1421
        c = UInt64(0)
64,988✔
1422
        for j = 0:63
64,988✔
1423
            c |= (UInt64(unchecked_bool_convert(C[ind])) << j)
4,159,232✔
1424
            ind += 1
4,159,232✔
1425
        end
8,253,476✔
1426
        Bc[bind] = c
64,988✔
1427
        bind += 1
64,988✔
1428
    end
120,908✔
1429

1430
    @inbounds if bind ≤ kd1
23,816✔
1431
        @assert bind == kd1
13,196✔
1432
        c = UInt64(0)
13,196✔
1433
        for j = 0:ld1
26,392✔
1434
            c |= (UInt64(unchecked_bool_convert(C[ind])) << j)
458,420✔
1435
            ind += 1
458,420✔
1436
        end
903,644✔
1437
        Bc[kd1] = (Bc[kd1] & msk_d1) | (c & ~msk_d1)
13,196✔
1438
    end
1439
end
1440

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

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

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

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

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

1492
        return B
1,261✔
1493
    end
1494
end
1495

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

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

1511
fill!(V::SubArray{Bool, <:Any, <:BitArray, <:Tuple{AbstractUnitRange{Int}, Vararg{Union{Int,AbstractUnitRange{Int}}}}}, x) =
15✔
1512
    _unsafe_fill_indices!(V.parent, x, V.indices...)
1513

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

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

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

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

1543
        return B
13✔
1544
    end
1545
end
1546

1547
## isassigned
1548

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

1565
isassigned(a::AbstractArray, i::CartesianIndex) = isassigned(a, Tuple(i)...)
6✔
1566
function isassigned(A::AbstractArray, i::Union{Integer, CartesianIndex}...)
5,572✔
1567
    isa(i, Tuple{Vararg{Int}}) || return isassigned(A, CartesianIndex(to_indices(A, i)))
5,572✔
1568
    @boundscheck checkbounds(Bool, A, i...) || return false
5,572✔
1569
    S = IndexStyle(A)
5,562✔
1570
    ninds = length(i)
5,562✔
1571
    if (isa(S, IndexLinear) && ninds != 1)
5,562✔
1572
        return @inbounds isassigned(A, _to_linear_index(A, i...))
2,045✔
1573
    elseif (!isa(S, IndexLinear) && ninds != ndims(A))
3,517✔
1574
        return @inbounds isassigned(A, _to_subscript_indices(A, i...)...)
4✔
1575
    else
1576
       try
3,513✔
1577
            A[i...]
3,758✔
1578
            true
3,513✔
1579
        catch e
1580
            if isa(e, BoundsError) || isa(e, UndefRefError)
26✔
1581
                return false
12✔
1582
            else
1583
                rethrow()
1✔
1584
            end
1585
        end
1586
    end
1587
end
1588

1589
## permutedims
1590

1591
## Permute array dims ##
1592

1593
function permutedims(B::StridedArray, perm)
241✔
1594
    dimsB = size(B)
241✔
1595
    ndimsB = length(dimsB)
241✔
1596
    (ndimsB == length(perm) && isperm(perm)) || throw(ArgumentError("no valid permutation of dimensions"))
241✔
1597
    dimsP = ntuple(i->dimsB[perm[i]], ndimsB)::typeof(dimsB)
946✔
1598
    P = similar(B, dimsP)
241✔
1599
    permutedims!(P, B, perm)
241✔
1600
end
1601

1602
function checkdims_perm(P::AbstractArray{TP,N}, B::AbstractArray{TB,N}, perm) where {TP,TB,N}
319✔
1603
    indsB = axes(B)
319✔
1604
    length(perm) == N || throw(ArgumentError("expected permutation of size $N, but length(perm)=$(length(perm))"))
319✔
1605
    isperm(perm) || throw(ArgumentError("input is not a permutation"))
324✔
1606
    indsP = axes(P)
314✔
1607
    for i = 1:length(perm)
417✔
1608
        indsP[i] == indsB[perm[i]] || throw(DimensionMismatch("destination tensor of incorrect size"))
861✔
1609
    end
1,408✔
1610
    nothing
314✔
1611
end
1612

1613
for (V, PT, BT) in Any[((:N,), BitArray, BitArray), ((:T,:N), Array, StridedArray)]
1614
    @eval @generated function permutedims!(P::$PT{$(V...)}, B::$BT{$(V...)}, perm) where $(V...)
246✔
1615
        quote
59✔
1616
            checkdims_perm(P, B, perm)
246✔
1617

1618
            #calculates all the strides
1619
            native_strides = size_to_strides(1, size(B)...)
246✔
1620
            strides_1 = 0
246✔
1621
            @nexprs $N d->(strides_{d+1} = native_strides[perm[d]])
246✔
1622

1623
            #Creates offset, because indexing starts at 1
1624
            offset = 1 - sum(@ntuple $N d->strides_{d+1})
246✔
1625

1626
            sumc = 0
246✔
1627
            ind = 1
246✔
1628
            @nloops($N, i, P,
1,577✔
1629
                    d->(sumc += i_d*strides_{d+1}), # PRE
1630
                    d->(sumc -= i_d*strides_{d+1}), # POST
1631
                    begin # BODY
1632
                        @inbounds P[ind] = B[sumc+offset]
231,113✔
1633
                        ind += 1
231,113✔
1634
                    end)
1635

1636
            return P
246✔
1637
        end
1638
    end
1639
end
1640

1641
## unique across dim
1642

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

1645
struct Prehashed
1646
    hash::UInt
893✔
1647
end
1648
hash(x::Prehashed) = x.hash
893✔
1649

1650
"""
1651
    unique(A::AbstractArray; dims::Int)
1652

1653
Return unique regions of `A` along dimension `dims`.
1654

1655
# Examples
1656
```jldoctest
1657
julia> A = map(isodd, reshape(Vector(1:8), (2,2,2)))
1658
2×2×2 Array{Bool, 3}:
1659
[:, :, 1] =
1660
 1  1
1661
 0  0
1662

1663
[:, :, 2] =
1664
 1  1
1665
 0  0
1666

1667
julia> unique(A)
1668
2-element Vector{Bool}:
1669
 1
1670
 0
1671

1672
julia> unique(A, dims=2)
1673
2×1×2 Array{Bool, 3}:
1674
[:, :, 1] =
1675
 1
1676
 0
1677

1678
[:, :, 2] =
1679
 1
1680
 0
1681

1682
julia> unique(A, dims=3)
1683
2×2×1 Array{Bool, 3}:
1684
[:, :, 1] =
1685
 1  1
1686
 0  0
1687
```
1688
"""
1689
unique(A::AbstractArray; dims::Union{Colon,Integer} = :) = _unique_dims(A, dims)
6,672✔
1690

1691
_unique_dims(A::AbstractArray, dims::Colon) = invoke(unique, Tuple{Any}, A)
3,325✔
1692

1693
@generated function _unique_dims(A::AbstractArray{T,N}, dim::Integer) where {T,N}
22✔
1694
    quote
5✔
1695
        1 <= dim <= $N || return copy(A)
11✔
1696
        hashes = zeros(UInt, axes(A, dim))
443✔
1697

1698
        # Compute hash for each row
1699
        k = 0
11✔
1700
        @nloops $N i A d->(if d == dim; k = i_d; end) begin
4,416✔
1701
            @inbounds hashes[k] = hash(hashes[k], hash((@nref $N A i)))
8,241✔
1702
        end
1703

1704
        # Collect index of first row for each hash
1705
        uniquerow = similar(Array{Int}, axes(A, dim))
13✔
1706
        firstrow = Dict{Prehashed,Int}()
11✔
1707
        for k = axes(A, dim)
22✔
1708
            uniquerow[k] = get!(firstrow, Prehashed(hashes[k]), k)
443✔
1709
        end
875✔
1710
        uniquerows = collect(values(firstrow))
11✔
1711

1712
        # Check for collisions
1713
        collided = falses(axes(A, dim))
11✔
1714
        @inbounds begin
11✔
1715
            @nloops $N i A d->(if d == dim
165✔
1716
                k = i_d
4,251✔
1717
                j_d = uniquerow[k]
4,251✔
1718
            else
1719
                j_d = i_d
4,195✔
1720
            end) begin
1721
                if !isequal((@nref $N A j), (@nref $N A i))
8,241✔
1722
                    collided[k] = true
170✔
1723
                end
1724
            end
1725
        end
1726

1727
        if any(collided)
28✔
1728
            nowcollided = similar(BitArray, axes(A, dim))
1✔
1729
            while any(collided)
20✔
1730
                # Collect index of first row for each collided hash
1731
                empty!(firstrow)
9✔
1732
                for j = axes(A, dim)
18✔
1733
                    collided[j] || continue
900✔
1734
                    uniquerow[j] = get!(firstrow, Prehashed(hashes[j]), j)
450✔
1735
                end
1,791✔
1736
                for v in values(firstrow)
18✔
1737
                    push!(uniquerows, v)
9✔
1738
                end
18✔
1739

1740
                # Check for collisions
1741
                fill!(nowcollided, false)
9✔
1742
                @nloops $N i A d->begin
90✔
1743
                    if d == dim
9,090✔
1744
                        k = i_d
9,000✔
1745
                        j_d = uniquerow[k]
9,000✔
1746
                        (!collided[k] || j_d == k) && continue
13,500✔
1747
                    else
1748
                        j_d = i_d
90✔
1749
                    end
1750
                end begin
1751
                    if !isequal((@nref $N A j), (@nref $N A i))
4,410✔
1752
                        nowcollided[k] = true
640✔
1753
                    end
1754
                end
1755
                (collided, nowcollided) = (nowcollided, collided)
9✔
1756
            end
9✔
1757
        end
1758

1759
        @nref $N A d->d == dim ? sort!(uniquerows) : (axes(A, d))
11✔
1760
    end
1761
end
1762

1763
# Show for pairs() with Cartesian indices. Needs to be here rather than show.jl for bootstrap order
1764
function Base.showarg(io::IO, r::Iterators.Pairs{<:Integer, <:Any, <:Any, T}, toplevel) where T <: Union{AbstractVector, Tuple}
1✔
1765
    print(io, "pairs(::$T)")
1✔
1766
end
1767
function Base.showarg(io::IO, r::Iterators.Pairs{<:CartesianIndex, <:Any, <:Any, T}, toplevel) where T <: AbstractArray
×
1768
    print(io, "pairs(::$T)")
×
1769
end
1770

1771
function Base.showarg(io::IO, r::Iterators.Pairs{<:CartesianIndex, <:Any, <:Any, T}, toplevel) where T<:AbstractVector
1✔
1772
    print(io, "pairs(IndexCartesian(), ::$T)")
1✔
1773
end
1774

1775
## sortslices
1776

1777
"""
1778
    sortslices(A; dims, alg::Algorithm=DEFAULT_UNSTABLE, lt=isless, by=identity, rev::Bool=false, order::Ordering=Forward)
1779

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

1784
E.g., if `A` is a matrix, `dims=1` will sort rows, `dims=2` will sort columns.
1785
Note that the default comparison function on one dimensional slices sorts
1786
lexicographically.
1787

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

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

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

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

1810
julia> sortslices([7 3 5; 6 -1 -4; 9 -2 8], dims=2) # Sort columns
1811
3×3 Matrix{Int64}:
1812
  3   5  7
1813
 -1  -4  6
1814
 -2   8  9
1815

1816
julia> sortslices([7 3 5; 6 -1 -4; 9 -2 8], dims=2, alg=InsertionSort, lt=(x,y)->isless(x[2],y[2]))
1817
3×3 Matrix{Int64}:
1818
  5   3  7
1819
 -4  -1  6
1820
  8  -2  9
1821

1822
julia> sortslices([7 3 5; 6 -1 -4; 9 -2 8], dims=2, rev=true)
1823
3×3 Matrix{Int64}:
1824
 7   5   3
1825
 6  -4  -1
1826
 9   8  -2
1827
```
1828

1829
# Higher dimensions
1830

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

1837
If `dims` is a tuple, the order of the dimensions in `dims` is
1838
relevant and specifies the linear order of the slices. E.g., if `A` is three
1839
dimensional and `dims` is `(1, 2)`, the orderings of the first two dimensions
1840
are re-arranged such that the slices (of the remaining third dimension) are sorted.
1841
If `dims` is `(2, 1)` instead, the same slices will be taken,
1842
but the result order will be row-major instead.
1843

1844
# Higher dimensional examples
1845
```
1846
julia> A = [4 3; 2 1 ;;; 'A' 'B'; 'C' 'D']
1847
2×2×2 Array{Any, 3}:
1848
[:, :, 1] =
1849
 4  3
1850
 2  1
1851

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

1856
julia> sortslices(A, dims=(1,2))
1857
2×2×2 Array{Any, 3}:
1858
[:, :, 1] =
1859
 1  3
1860
 2  4
1861

1862
[:, :, 2] =
1863
 'D'  'B'
1864
 'C'  'A'
1865

1866
julia> sortslices(A, dims=(2,1))
1867
2×2×2 Array{Any, 3}:
1868
[:, :, 1] =
1869
 1  2
1870
 3  4
1871

1872
[:, :, 2] =
1873
 'D'  'C'
1874
 'B'  'A'
1875

1876
julia> sortslices(reshape([5; 4; 3; 2; 1], (1,1,5)), dims=3, by=x->x[1,1])
1877
1×1×5 Array{Int64, 3}:
1878
[:, :, 1] =
1879
 1
1880

1881
[:, :, 2] =
1882
 2
1883

1884
[:, :, 3] =
1885
 3
1886

1887
[:, :, 4] =
1888
 4
1889

1890
[:, :, 5] =
1891
 5
1892
```
1893
"""
1894
function sortslices(A::AbstractArray; dims::Union{Integer, Tuple{Vararg{Integer}}}, kws...)
28✔
1895
    _sortslices(A, Val{dims}(); kws...)
14✔
1896
end
1897

1898
# Works around inference's lack of ability to recognize partial constness
1899
struct DimSelector{dims, T}
1900
    A::T
14✔
1901
end
1902
DimSelector{dims}(x::T) where {dims, T} = DimSelector{dims, T}(x)
14✔
1903
(ds::DimSelector{dims, T})(i) where {dims, T} = i in dims ? axes(ds.A, i) : (:,)
36✔
1904

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

1907
function compute_itspace(A, ::Val{dims}) where {dims}
14✔
1908
    negdims = _negdims(ndims(A), dims)
14✔
1909
    axs = Iterators.product(ntuple(DimSelector{dims}(A), ndims(A))...)
18✔
1910
    vec(permutedims(collect(axs), (dims..., negdims...)))
14✔
1911
end
1912

1913
function _sortslices(A::AbstractArray, d::Val{dims}; kws...) where dims
28✔
1914
    itspace = compute_itspace(A, d)
14✔
1915
    vecs = map(its->view(A, its...), itspace)
78✔
1916
    p = sortperm(vecs; kws...)
14✔
1917
    if ndims(A) == 2 && isa(dims, Integer) && isa(A, Array)
14✔
1918
        # At the moment, the performance of the generic version is subpar
1919
        # (about 5x slower). Hardcode a fast-path until we're able to
1920
        # optimize this.
1921
        return dims == 1 ? A[p, :] : A[:, p]
8✔
1922
    else
1923
        B = similar(A)
8✔
1924
        for (x, its) in zip(p, itspace)
8✔
1925
            B[its...] = vecs[x]
24✔
1926
        end
30✔
1927
        B
6✔
1928
    end
1929
end
1930

1931
getindex(b::Ref, ::CartesianIndex{0}) = getindex(b)
4✔
1932
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