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

JuliaLang / julia / #37997

29 Jan 2025 02:08AM UTC coverage: 17.283% (-68.7%) from 85.981%
#37997

push

local

web-flow
bpart: Start enforcing min_world for global variable definitions (#57150)

This is the analog of #57102 for global variables. Unlike for consants,
there is no automatic global backdate mechanism. The reasoning for this
is that global variables can be declared at any time, unlike constants
which can only be decalared once their value is available. As a result
code patterns using `Core.eval` to declare globals are rarer and likely
incorrect.

1 of 22 new or added lines in 3 files covered. (4.55%)

31430 existing lines in 188 files now uncovered.

7903 of 45728 relevant lines covered (17.28%)

98663.7 hits per line

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

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

3
## Basic functions ##
4

5
"""
6
    AbstractArray{T,N}
7

8
Supertype for `N`-dimensional arrays (or array-like types) with elements of type `T`.
9
[`Array`](@ref) and other types are subtypes of this. See the manual section on the
10
[`AbstractArray` interface](@ref man-interface-array).
11

12
See also: [`AbstractVector`](@ref), [`AbstractMatrix`](@ref), [`eltype`](@ref), [`ndims`](@ref).
13
"""
14
AbstractArray
15

UNCOV
16
convert(::Type{T}, a::T) where {T<:AbstractArray} = a
×
UNCOV
17
convert(::Type{AbstractArray{T}}, a::AbstractArray) where {T} = AbstractArray{T}(a)::AbstractArray{T}
×
UNCOV
18
convert(::Type{AbstractArray{T,N}}, a::AbstractArray{<:Any,N}) where {T,N} = AbstractArray{T,N}(a)::AbstractArray{T,N}
×
19

20
"""
21
    size(A::AbstractArray, [dim])
22

23
Return a tuple containing the dimensions of `A`. Optionally you can specify a
24
dimension to just get the length of that dimension.
25

26
Note that `size` may not be defined for arrays with non-standard indices, in which case [`axes`](@ref)
27
may be useful. See the manual chapter on [arrays with custom indices](@ref man-custom-indices).
28

29
See also: [`length`](@ref), [`ndims`](@ref), [`eachindex`](@ref), [`sizeof`](@ref).
30

31
# Examples
32
```jldoctest
33
julia> A = fill(1, (2,3,4));
34

35
julia> size(A)
36
(2, 3, 4)
37

38
julia> size(A, 2)
39
3
40
```
41
"""
42
size(t::AbstractArray{T,N}, d) where {T,N} = d::Integer <= N ? size(t)[d] : 1
13,383✔
43

44
"""
45
    axes(A, d)
46

47
Return the valid range of indices for array `A` along dimension `d`.
48

49
See also [`size`](@ref), and the manual chapter on [arrays with custom indices](@ref man-custom-indices).
50

51
# Examples
52

53
```jldoctest
54
julia> A = fill(1, (5,6,7));
55

56
julia> axes(A, 2)
57
Base.OneTo(6)
58

59
julia> axes(A, 4) == 1:1  # all dimensions d > ndims(A) have size 1
60
true
61
```
62

63
# Usage note
64

65
Each of the indices has to be an `AbstractUnitRange{<:Integer}`, but at the same time can be
66
a type that uses custom indices. So, for example, if you need a subset, use generalized
67
indexing constructs like `begin`/`end` or [`firstindex`](@ref)/[`lastindex`](@ref):
68

69
```julia
70
ix = axes(v, 1)
71
ix[2:end]          # will work for eg Vector, but may fail in general
72
ix[(begin+1):end]  # works for generalized indexes
73
```
74
"""
75
function axes(A::AbstractArray{T,N}, d) where {T,N}
76
    @inline
43,197✔
77
    d::Integer <= N ? axes(A)[d] : OneTo(1)
64,701✔
78
end
79

80
"""
81
    axes(A)
82

83
Return the tuple of valid indices for array `A`.
84

85
See also: [`size`](@ref), [`keys`](@ref), [`eachindex`](@ref).
86

87
# Examples
88

89
```jldoctest
90
julia> A = fill(1, (5,6,7));
91

92
julia> axes(A)
93
(Base.OneTo(5), Base.OneTo(6), Base.OneTo(7))
94
```
95
"""
96
function axes(A)
1✔
97
    @inline
37,327✔
98
    map(unchecked_oneto, size(A))
4,980,094✔
99
end
100

101
"""
102
    has_offset_axes(A)
103
    has_offset_axes(A, B, ...)
104

105
Return `true` if the indices of `A` start with something other than 1 along any axis.
106
If multiple arguments are passed, equivalent to `has_offset_axes(A) || has_offset_axes(B) || ...`.
107

108
See also [`require_one_based_indexing`](@ref).
109
"""
110
has_offset_axes() = false
×
UNCOV
111
has_offset_axes(A) = _any_tuple(x->Int(first(x))::Int != 1, false, axes(A)...)
×
UNCOV
112
has_offset_axes(A::AbstractVector) = Int(firstindex(A))::Int != 1 # improve performance of a common case (ranges)
×
113
has_offset_axes(::Colon) = false
×
114
has_offset_axes(::Array) = false
×
115
# note: this could call `any` directly if the compiler can infer it. We don't use _any_tuple
116
# here because it stops full elision in some cases (#49332) and we don't need handling of
117
# `missing` (has_offset_axes(A) always returns a Bool)
UNCOV
118
has_offset_axes(A, As...) = has_offset_axes(A) || has_offset_axes(As...)
×
119

120

121
"""
122
    require_one_based_indexing(A::AbstractArray)
123
    require_one_based_indexing(A,B...)
124

125
Throw an `ArgumentError` if the indices of any argument start with something other than `1` along any axis.
126
See also [`has_offset_axes`](@ref).
127

128
!!! compat "Julia 1.2"
129
     This function requires at least Julia 1.2.
130
"""
131
require_one_based_indexing(A...) = !has_offset_axes(A...) || throw(ArgumentError("offset arrays are not supported but got an array with index other than 1"))
33✔
132

133
# Performance optimization: get rid of a branch on `d` in `axes(A, d)`
134
# for d=1. 1d arrays are heavily used, and the first dimension comes up
135
# in other applications.
136
axes1(A::AbstractArray{<:Any,0}) = OneTo(1)
×
137
axes1(A::AbstractArray) = (@inline; axes(A)[1])
2,023,112✔
UNCOV
138
axes1(iter) = oneto(length(iter))
×
139

140
"""
141
    keys(a::AbstractArray)
142

143
Return an efficient array describing all valid indices for `a` arranged in the shape of `a` itself.
144

145
The keys of 1-dimensional arrays (vectors) are integers, whereas all other N-dimensional
146
arrays use [`CartesianIndex`](@ref) to describe their locations.  Often the special array
147
types [`LinearIndices`](@ref) and [`CartesianIndices`](@ref) are used to efficiently
148
represent these arrays of integers and `CartesianIndex`es, respectively.
149

150
Note that the `keys` of an array might not be the most efficient index type; for maximum
151
performance use  [`eachindex`](@ref) instead.
152

153
# Examples
154
```jldoctest
155
julia> keys([4, 5, 6])
156
3-element LinearIndices{1, Tuple{Base.OneTo{Int64}}}:
157
 1
158
 2
159
 3
160

161
julia> keys([4 5; 6 7])
162
CartesianIndices((2, 2))
163
```
164
"""
UNCOV
165
keys(a::AbstractArray) = CartesianIndices(axes(a))
×
166
keys(a::AbstractVector) = LinearIndices(a)
1,859✔
167

168
"""
169
    keytype(T::Type{<:AbstractArray})
170
    keytype(A::AbstractArray)
171

172
Return the key type of an array. This is equal to the
173
[`eltype`](@ref) of the result of `keys(...)`, and is provided
174
mainly for compatibility with the dictionary interface.
175

176
# Examples
177
```jldoctest
178
julia> keytype([1, 2, 3]) == Int
179
true
180

181
julia> keytype([1 2; 3 4])
182
CartesianIndex{2}
183
```
184

185
!!! compat "Julia 1.2"
186
     For arrays, this function requires at least Julia 1.2.
187
"""
UNCOV
188
keytype(a::AbstractArray) = keytype(typeof(a))
×
189
keytype(::Type{Union{}}, slurp...) = eltype(Union{})
×
190

UNCOV
191
keytype(A::Type{<:AbstractArray}) = CartesianIndex{ndims(A)}
×
UNCOV
192
keytype(A::Type{<:AbstractVector}) = Int
×
193

UNCOV
194
valtype(a::AbstractArray) = valtype(typeof(a))
×
195
valtype(::Type{Union{}}, slurp...) = eltype(Union{})
×
196

197
"""
198
    valtype(T::Type{<:AbstractArray})
199
    valtype(A::AbstractArray)
200

201
Return the value type of an array. This is identical to [`eltype`](@ref) and is
202
provided mainly for compatibility with the dictionary interface.
203

204
# Examples
205
```jldoctest
206
julia> valtype(["one", "two", "three"])
207
String
208
```
209

210
!!! compat "Julia 1.2"
211
     For arrays, this function requires at least Julia 1.2.
212
"""
UNCOV
213
valtype(A::Type{<:AbstractArray}) = eltype(A)
×
214

215
prevind(::AbstractArray, i::Integer) = Int(i)-1
386✔
216
nextind(::AbstractArray, i::Integer) = Int(i)+1
16,898✔
217

218

219
"""
220
    eltype(type)
221

222
Determine the type of the elements generated by iterating a collection of the given `type`.
223
For dictionary types, this will be a `Pair{KeyType,ValType}`. The definition
224
`eltype(x) = eltype(typeof(x))` is provided for convenience so that instances can be passed
225
instead of types. However the form that accepts a type argument should be defined for new
226
types.
227

228
See also: [`keytype`](@ref), [`typeof`](@ref).
229

230
# Examples
231
```jldoctest
232
julia> eltype(fill(1f0, (2,2)))
233
Float32
234

235
julia> eltype(fill(0x1, (2,2)))
236
UInt8
237
```
238
"""
239
eltype(::Type) = Any
18✔
UNCOV
240
eltype(::Type{Bottom}, slurp...) = throw(ArgumentError("Union{} does not have elements"))
×
241
eltype(x) = eltype(typeof(x))
244✔
242
eltype(::Type{<:AbstractArray{E}}) where {E} = @isdefined(E) ? E : Any
224✔
243

244
"""
245
    elsize(type)
246

247
Compute the memory stride in bytes between consecutive elements of [`eltype`](@ref)
248
stored inside the given `type`, if the array elements are stored densely with a
249
uniform linear stride.
250

251
# Examples
252
```jldoctest
253
julia> Base.elsize(rand(Float32, 10))
254
4
255
```
256
"""
257
elsize(A::AbstractArray) = elsize(typeof(A))
12✔
258

259
"""
260
    ndims(A::AbstractArray) -> Integer
261

262
Return the number of dimensions of `A`.
263

264
See also: [`size`](@ref), [`axes`](@ref).
265

266
# Examples
267
```jldoctest
268
julia> A = fill(1, (3,4,5));
269

270
julia> ndims(A)
271
3
272
```
273
"""
274
ndims(::AbstractArray{T,N}) where {T,N} = N::Int
3✔
UNCOV
275
ndims(::Type{<:AbstractArray{<:Any,N}}) where {N} = N::Int
×
276
ndims(::Type{Union{}}, slurp...) = throw(ArgumentError("Union{} does not have elements"))
×
277

278
"""
279
    length(collection) -> Integer
280

281
Return the number of elements in the collection.
282

283
Use [`lastindex`](@ref) to get the last valid index of an indexable collection.
284

285
See also: [`size`](@ref), [`ndims`](@ref), [`eachindex`](@ref).
286

287
# Examples
288
```jldoctest
289
julia> length(1:5)
290
5
291

292
julia> length([1, 2, 3, 4])
293
4
294

295
julia> length([1 2; 3 4])
296
4
297
```
298
"""
299
length
300

301
"""
302
    length(A::AbstractArray)
303

304
Return the number of elements in the array, defaults to `prod(size(A))`.
305

306
# Examples
307
```jldoctest
308
julia> length([1, 2, 3, 4])
309
4
310

311
julia> length([1 2; 3 4])
312
4
313
```
314
"""
315
length(t::AbstractArray) = (@inline; prod(size(t)))
860,852✔
316

317
# `eachindex` is mostly an optimization of `keys`
UNCOV
318
eachindex(itrs...) = keys(itrs...)
×
319

320
# eachindex iterates over all indices. IndexCartesian definitions are later.
321
eachindex(A::AbstractVector) = (@inline(); axes1(A))
147,014✔
322

323

UNCOV
324
@noinline function throw_eachindex_mismatch_indices(::IndexLinear, inds...)
×
UNCOV
325
    throw(DimensionMismatch("all inputs to eachindex must have the same indices, got $(join(inds, ", ", " and "))"))
×
326
end
UNCOV
327
@noinline function throw_eachindex_mismatch_indices(::IndexCartesian, inds...)
×
UNCOV
328
    throw(DimensionMismatch("all inputs to eachindex must have the same axes, got $(join(inds, ", ", " and "))"))
×
329
end
330

331
"""
332
    eachindex(A...)
333
    eachindex(::IndexStyle, A::AbstractArray...)
334

335
Create an iterable object for visiting each index of an `AbstractArray` `A` in an efficient
336
manner. For array types that have opted into fast linear indexing (like `Array`), this is
337
simply the range `1:length(A)` if they use 1-based indexing.
338
For array types that have not opted into fast linear indexing, a specialized Cartesian
339
range is typically returned to efficiently index into the array with indices specified
340
for every dimension.
341

342
In general `eachindex` accepts arbitrary iterables, including strings and dictionaries, and returns
343
an iterator object supporting arbitrary index types (e.g. unevenly spaced or non-integer indices).
344

345
If `A` is `AbstractArray` it is possible to explicitly specify the style of the indices that
346
should be returned by `eachindex` by passing a value having `IndexStyle` type as its first argument
347
(typically `IndexLinear()` if linear indices are required or `IndexCartesian()` if Cartesian
348
range is wanted).
349

350
If you supply more than one `AbstractArray` argument, `eachindex` will create an
351
iterable object that is fast for all arguments (typically a [`UnitRange`](@ref)
352
if all inputs have fast linear indexing, a [`CartesianIndices`](@ref) otherwise).
353
If the arrays have different sizes and/or dimensionalities, a `DimensionMismatch` exception
354
will be thrown.
355

356
See also [`pairs`](@ref)`(A)` to iterate over indices and values together,
357
and [`axes`](@ref)`(A, 2)` for valid indices along one dimension.
358

359
# Examples
360
```jldoctest
361
julia> A = [10 20; 30 40];
362

363
julia> for i in eachindex(A) # linear indexing
364
           println("A[", i, "] == ", A[i])
365
       end
366
A[1] == 10
367
A[2] == 30
368
A[3] == 20
369
A[4] == 40
370

371
julia> for i in eachindex(view(A, 1:2, 1:1)) # Cartesian indexing
372
           println(i)
373
       end
374
CartesianIndex(1, 1)
375
CartesianIndex(2, 1)
376
```
377
"""
UNCOV
378
eachindex(A::AbstractArray) = (@inline(); eachindex(IndexStyle(A), A))
×
379

UNCOV
380
function eachindex(A::AbstractArray, B::AbstractArray)
×
UNCOV
381
    @inline
×
UNCOV
382
    eachindex(IndexStyle(A,B), A, B)
×
383
end
UNCOV
384
function eachindex(A::AbstractArray, B::AbstractArray...)
×
UNCOV
385
    @inline
×
UNCOV
386
    eachindex(IndexStyle(A,B...), A, B...)
×
387
end
UNCOV
388
eachindex(::IndexLinear, A::AbstractArray) = (@inline; oneto(length(A)))
×
389
eachindex(::IndexLinear, A::AbstractVector) = (@inline; axes1(A))
1,863,283✔
UNCOV
390
function eachindex(::IndexLinear, A::AbstractArray, B::AbstractArray...)
×
UNCOV
391
    @inline
×
UNCOV
392
    indsA = eachindex(IndexLinear(), A)
×
UNCOV
393
    _all_match_first(X->eachindex(IndexLinear(), X), indsA, B...) ||
×
394
        throw_eachindex_mismatch_indices(IndexLinear(), eachindex(A), eachindex.(B)...)
UNCOV
395
    indsA
×
396
end
UNCOV
397
function _all_match_first(f::F, inds, A, B...) where F<:Function
×
UNCOV
398
    @inline
×
UNCOV
399
    (inds == f(A)) & _all_match_first(f, inds, B...)
×
400
end
401
_all_match_first(f::F, inds) where F<:Function = true
×
402

403
# keys with an IndexStyle
404
keys(s::IndexStyle, A::AbstractArray, B::AbstractArray...) = eachindex(s, A, B...)
×
405

406
"""
407
    lastindex(collection) -> Integer
408
    lastindex(collection, d) -> Integer
409

410
Return the last index of `collection`. If `d` is given, return the last index of `collection` along dimension `d`.
411

412
The syntaxes `A[end]` and `A[end, end]` lower to `A[lastindex(A)]` and
413
`A[lastindex(A, 1), lastindex(A, 2)]`, respectively.
414

415
See also: [`axes`](@ref), [`firstindex`](@ref), [`eachindex`](@ref), [`prevind`](@ref).
416

417
# Examples
418
```jldoctest
419
julia> lastindex([1,2,4])
420
3
421

422
julia> lastindex(rand(3,4,5), 2)
423
4
424
```
425
"""
426
lastindex(a::AbstractArray) = (@inline; last(eachindex(IndexLinear(), a)))
170,919✔
427
lastindex(a, d) = (@inline; last(axes(a, d)))
1,119✔
428

429
"""
430
    firstindex(collection) -> Integer
431
    firstindex(collection, d) -> Integer
432

433
Return the first index of `collection`. If `d` is given, return the first index of `collection` along dimension `d`.
434

435
The syntaxes `A[begin]` and `A[1, begin]` lower to `A[firstindex(A)]` and
436
`A[1, firstindex(A, 2)]`, respectively.
437

438
See also: [`first`](@ref), [`axes`](@ref), [`lastindex`](@ref), [`nextind`](@ref).
439

440
# Examples
441
```jldoctest
442
julia> firstindex([1,2,4])
443
1
444

445
julia> firstindex(rand(3,4,5), 2)
446
1
447
```
448
"""
449
firstindex(a::AbstractArray) = (@inline; first(eachindex(IndexLinear(), a)))
11,904✔
UNCOV
450
firstindex(a, d) = (@inline; first(axes(a, d)))
×
451

452
@propagate_inbounds first(a::AbstractArray) = a[first(eachindex(a))]
165✔
453

454
"""
455
    first(coll)
456

457
Get the first element of an iterable collection. Return the start point of an
458
[`AbstractRange`](@ref) even if it is empty.
459

460
See also: [`only`](@ref), [`firstindex`](@ref), [`last`](@ref).
461

462
# Examples
463
```jldoctest
464
julia> first(2:2:10)
465
2
466

467
julia> first([1; 2; 3; 4])
468
1
469
```
470
"""
UNCOV
471
function first(itr)
×
UNCOV
472
    x = iterate(itr)
×
UNCOV
473
    x === nothing && throw(ArgumentError("collection must be non-empty"))
×
UNCOV
474
    x[1]
×
475
end
476

477
"""
478
    first(itr, n::Integer)
479

480
Get the first `n` elements of the iterable collection `itr`, or fewer elements if `itr` is not
481
long enough.
482

483
See also: [`startswith`](@ref), [`Iterators.take`](@ref).
484

485
!!! compat "Julia 1.6"
486
    This method requires at least Julia 1.6.
487

488
# Examples
489
```jldoctest
490
julia> first(["foo", "bar", "qux"], 2)
491
2-element Vector{String}:
492
 "foo"
493
 "bar"
494

495
julia> first(1:6, 10)
496
1:6
497

498
julia> first(Bool[], 1)
499
Bool[]
500
```
501
"""
UNCOV
502
first(itr, n::Integer) = collect(Iterators.take(itr, n))
×
503
# Faster method for vectors
UNCOV
504
function first(v::AbstractVector, n::Integer)
×
UNCOV
505
    n < 0 && throw(ArgumentError("Number of elements must be non-negative"))
×
UNCOV
506
    v[range(begin, length=min(n, checked_length(v)))]
×
507
end
508

509
"""
510
    last(coll)
511

512
Get the last element of an ordered collection, if it can be computed in O(1) time. This is
513
accomplished by calling [`lastindex`](@ref) to get the last index. Return the end
514
point of an [`AbstractRange`](@ref) even if it is empty.
515

516
See also [`first`](@ref), [`endswith`](@ref).
517

518
# Examples
519
```jldoctest
520
julia> last(1:2:10)
521
9
522

523
julia> last([1; 2; 3; 4])
524
4
525
```
526
"""
527
last(a) = a[end]
784✔
528

529
"""
530
    last(itr, n::Integer)
531

532
Get the last `n` elements of the iterable collection `itr`, or fewer elements if `itr` is not
533
long enough.
534

535
!!! compat "Julia 1.6"
536
    This method requires at least Julia 1.6.
537

538
# Examples
539
```jldoctest
540
julia> last(["foo", "bar", "qux"], 2)
541
2-element Vector{String}:
542
 "bar"
543
 "qux"
544

545
julia> last(1:6, 10)
546
1:6
547

548
julia> last(Float64[], 1)
549
Float64[]
550
```
551
"""
UNCOV
552
last(itr, n::Integer) = reverse!(collect(Iterators.take(Iterators.reverse(itr), n)))
×
553
# Faster method for arrays
554
function last(v::AbstractVector, n::Integer)
UNCOV
555
    n < 0 && throw(ArgumentError("Number of elements must be non-negative"))
×
UNCOV
556
    v[range(stop=lastindex(v), length=min(n, checked_length(v)))]
×
557
end
558

559
"""
560
    strides(A)
561

562
Return a tuple of the memory strides in each dimension.
563

564
See also: [`stride`](@ref).
565

566
# Examples
567
```jldoctest
568
julia> A = fill(1, (3,4,5));
569

570
julia> strides(A)
571
(1, 3, 12)
572
```
573
"""
574
function strides end
575

576
"""
577
    stride(A, k::Integer)
578

579
Return the distance in memory (in number of elements) between adjacent elements in dimension `k`.
580

581
See also: [`strides`](@ref).
582

583
# Examples
584
```jldoctest
585
julia> A = fill(1, (3,4,5));
586

587
julia> stride(A,2)
588
3
589

590
julia> stride(A,3)
591
12
592
```
593
"""
UNCOV
594
function stride(A::AbstractArray, k::Integer)
×
UNCOV
595
    st = strides(A)
×
UNCOV
596
    k ≤ ndims(A) && return st[k]
×
UNCOV
597
    ndims(A) == 0 && return 1
×
UNCOV
598
    sz = size(A)
×
UNCOV
599
    s = st[1] * sz[1]
×
UNCOV
600
    for i in 2:ndims(A)
×
UNCOV
601
        s += st[i] * sz[i]
×
UNCOV
602
    end
×
UNCOV
603
    return s
×
604
end
605

UNCOV
606
@inline size_to_strides(s, d, sz...) = (s, size_to_strides(s * d, sz...)...)
×
UNCOV
607
size_to_strides(s, d) = (s,)
×
UNCOV
608
size_to_strides(s) = ()
×
609

UNCOV
610
function isstored(A::AbstractArray{<:Any,N}, I::Vararg{Integer,N}) where {N}
×
UNCOV
611
    @boundscheck checkbounds(A, I...)
×
UNCOV
612
    return true
×
613
end
614

615
# used to compute "end" for last index
616
function trailingsize(A, n)
×
617
    s = 1
×
618
    for i=n:ndims(A)
×
619
        s *= size(A,i)
×
620
    end
×
621
    return s
×
622
end
623
function trailingsize(inds::Indices, n)
×
624
    s = 1
×
625
    for i=n:length(inds)
×
626
        s *= length(inds[i])
×
627
    end
×
628
    return s
×
629
end
630
# This version is type-stable even if inds is heterogeneous
631
function trailingsize(inds::Indices)
×
632
    @inline
×
633
    prod(map(length, inds))
×
634
end
635

636
## Bounds checking ##
637

638
# The overall hierarchy is
639
#     `checkbounds(A, I...)` ->
640
#         `checkbounds(Bool, A, I...)` ->
641
#             `checkbounds_indices(Bool, IA, I)`, which recursively calls
642
#                 `checkindex` for each dimension
643
#
644
# See the "boundscheck" devdocs for more information.
645
#
646
# Note this hierarchy has been designed to reduce the likelihood of
647
# method ambiguities.  We try to make `checkbounds` the place to
648
# specialize on array type, and try to avoid specializations on index
649
# types; conversely, `checkindex` is intended to be specialized only
650
# on index type (especially, its last argument).
651

652
"""
653
    checkbounds(Bool, A, I...)
654

655
Return `true` if the specified indices `I` are in bounds for the given
656
array `A`. Subtypes of `AbstractArray` should specialize this method
657
if they need to provide custom bounds checking behaviors; however, in
658
many cases one can rely on `A`'s indices and [`checkindex`](@ref).
659

660
See also [`checkindex`](@ref).
661

662
# Examples
663
```jldoctest
664
julia> A = rand(3, 3);
665

666
julia> checkbounds(Bool, A, 2)
667
true
668

669
julia> checkbounds(Bool, A, 3, 4)
670
false
671

672
julia> checkbounds(Bool, A, 1:3)
673
true
674

675
julia> checkbounds(Bool, A, 1:3, 2:4)
676
false
677
```
678
"""
679
function checkbounds(::Type{Bool}, A::AbstractArray, I...)
UNCOV
680
    @inline
×
681
    checkbounds_indices(Bool, axes(A), I)
121,569✔
682
end
683

684
# Linear indexing is explicitly allowed when there is only one (non-cartesian) index;
685
# indices that do not allow linear indexing (e.g., logical arrays, cartesian indices, etc)
686
# must add specialized methods to implement their restrictions
687
function checkbounds(::Type{Bool}, A::AbstractArray, i)
688
    @inline
405,038✔
689
    return checkindex(Bool, eachindex(IndexLinear(), A), i)
3,120,999✔
690
end
691

692
"""
693
    checkbounds(A, I...)
694

695
Throw an error if the specified indices `I` are not in bounds for the given array `A`.
696
"""
697
function checkbounds(A::AbstractArray, I...)
698
    @inline
405,037✔
699
    checkbounds(Bool, A, I...) || throw_boundserror(A, I)
1,801,970✔
700
    nothing
405,037✔
701
end
702

703
"""
704
    checkbounds_indices(Bool, IA, I)
705

706
Return `true` if the "requested" indices in the tuple `I` fall within
707
the bounds of the "permitted" indices specified by the tuple
708
`IA`. This function recursively consumes elements of these tuples,
709
usually in a 1-for-1 fashion,
710

711
    checkbounds_indices(Bool, (IA1, IA...), (I1, I...)) = checkindex(Bool, IA1, I1) &
712
                                                          checkbounds_indices(Bool, IA, I)
713

714
Note that [`checkindex`](@ref) is being used to perform the actual
715
bounds-check for a single dimension of the array.
716

717
There are two important exceptions to the 1-1 rule: linear indexing and
718
CartesianIndex{N}, both of which may "consume" more than one element
719
of `IA`.
720

721
See also [`checkbounds`](@ref).
722
"""
723
function checkbounds_indices(::Type{Bool}, inds::Tuple, I::Tuple{Any, Vararg})
UNCOV
724
    @inline
×
725
    return checkindex(Bool, get(inds, 1, OneTo(1)), I[1])::Bool &
242,562✔
726
        checkbounds_indices(Bool, safe_tail(inds), tail(I))
727
end
728

UNCOV
729
checkbounds_indices(::Type{Bool}, inds::Tuple, ::Tuple{}) = (@inline; all(x->length(x)==1, inds))
×
730

731
# check along a single dimension
732
"""
733
    checkindex(Bool, inds::AbstractUnitRange, index)
734

735
Return `true` if the given `index` is within the bounds of
736
`inds`. Custom types that would like to behave as indices for all
737
arrays can extend this method in order to provide a specialized bounds
738
checking implementation.
739

740
See also [`checkbounds`](@ref).
741

742
# Examples
743
```jldoctest
744
julia> checkindex(Bool, 1:20, 8)
745
true
746

747
julia> checkindex(Bool, 1:20, 21)
748
false
749
```
750
"""
UNCOV
751
checkindex(::Type{Bool}, inds, i) = throw(ArgumentError(LazyString("unable to check bounds for indices of type ", typeof(i))))
×
752
checkindex(::Type{Bool}, inds::AbstractUnitRange, i::Real) = (first(inds) <= i) & (i <= last(inds))
1,440,708✔
UNCOV
753
checkindex(::Type{Bool}, inds::IdentityUnitRange, i::Real) = checkindex(Bool, inds.indices, i)
×
754
checkindex(::Type{Bool}, inds::OneTo{T}, i::T) where {T<:BitInteger} = unsigned(i - one(i)) < unsigned(last(inds))
2,592,294✔
755
checkindex(::Type{Bool}, inds::AbstractUnitRange, ::Colon) = true
×
756
checkindex(::Type{Bool}, inds::AbstractUnitRange, ::Slice) = true
×
757
checkindex(::Type{Bool}, inds::AbstractUnitRange, i::AbstractRange) =
1,419,732✔
758
    isempty(i) | (checkindex(Bool, inds, first(i)) & checkindex(Bool, inds, last(i)))
759
# range like indices with cheap `extrema`
UNCOV
760
checkindex(::Type{Bool}, inds::AbstractUnitRange, i::LinearIndices) =
×
761
    isempty(i) | (checkindex(Bool, inds, first(i)) & checkindex(Bool, inds, last(i)))
762

763
function checkindex(::Type{Bool}, inds, I::AbstractArray)
UNCOV
764
    @inline
×
UNCOV
765
    b = true
×
766
    for i in I
2,476✔
767
        b &= checkindex(Bool, inds, i)
27,458✔
768
    end
27,458✔
769
    b
2,476✔
770
end
771

772
# See also specializations in multidimensional
773

774
## Constructors ##
775

776
# default arguments to similar()
777
"""
778
    similar(array, [element_type=eltype(array)], [dims=size(array)])
779

780
Create an uninitialized mutable array with the given element type and size, based upon the
781
given source array. The second and third arguments are both optional, defaulting to the
782
given array's `eltype` and `size`. The dimensions may be specified either as a single tuple
783
argument or as a series of integer arguments.
784

785
Custom AbstractArray subtypes may choose which specific array type is best-suited to return
786
for the given element type and dimensionality. If they do not specialize this method, the
787
default is an `Array{element_type}(undef, dims...)`.
788

789
For example, `similar(1:10, 1, 4)` returns an uninitialized `Array{Int,2}` since ranges are
790
neither mutable nor support 2 dimensions:
791

792
```julia-repl
793
julia> similar(1:10, 1, 4)
794
1×4 Matrix{Int64}:
795
 4419743872  4374413872  4419743888  0
796
```
797

798
Conversely, `similar(trues(10,10), 2)` returns an uninitialized `BitVector` with two
799
elements since `BitArray`s are both mutable and can support 1-dimensional arrays:
800

801
```julia-repl
802
julia> similar(trues(10,10), 2)
803
2-element BitVector:
804
 0
805
 0
806
```
807

808
Since `BitArray`s can only store elements of type [`Bool`](@ref), however, if you request a
809
different element type it will create a regular `Array` instead:
810

811
```julia-repl
812
julia> similar(falses(10), Float64, 2, 4)
813
2×4 Matrix{Float64}:
814
 2.18425e-314  2.18425e-314  2.18425e-314  2.18425e-314
815
 2.18425e-314  2.18425e-314  2.18425e-314  2.18425e-314
816
```
817

818
See also: [`undef`](@ref), [`isassigned`](@ref).
819
"""
UNCOV
820
similar(a::AbstractArray{T}) where {T}                             = similar(a, T)
×
UNCOV
821
similar(a::AbstractArray, ::Type{T}) where {T}                     = similar(a, T, to_shape(axes(a)))
×
822
similar(a::AbstractArray{T}, dims::Tuple) where {T}                = similar(a, T, to_shape(dims))
515,675✔
UNCOV
823
similar(a::AbstractArray{T}, dims::DimOrInd...) where {T}          = similar(a, T, to_shape(dims))
×
824
similar(a::AbstractArray, ::Type{T}, dims::DimOrInd...) where {T}  = similar(a, T, to_shape(dims))
13,086✔
825
# Similar supports specifying dims as either Integers or AbstractUnitRanges or any mixed combination
826
# thereof. Ideally, we'd just convert Integers to OneTos and then call a canonical method with the axes,
827
# but we don't want to require all AbstractArray subtypes to dispatch on Base.OneTo. So instead we
828
# define this method to convert supported axes to Ints, with the expectation that an offset array
829
# package will define a method with dims::Tuple{Union{Integer, UnitRange}, Vararg{Union{Integer, UnitRange}}}
830
similar(a::AbstractArray, ::Type{T}, dims::Tuple{Union{Integer, OneTo}, Vararg{Union{Integer, OneTo}}}) where {T} = similar(a, T, to_shape(dims))
180✔
831
# similar creates an Array by default
832
similar(a::AbstractArray, ::Type{T}, dims::Dims{N}) where {T,N}    = Array{T,N}(undef, dims)
13,082✔
833

834
to_shape(::Tuple{}) = ()
×
UNCOV
835
to_shape(dims::Dims) = dims
×
836
to_shape(dims::DimsOrInds) = map(to_shape, dims)::DimsOrInds
6,532✔
837
# each dimension
UNCOV
838
to_shape(i::Int) = i
×
UNCOV
839
to_shape(i::Integer) = Int(i)
×
840
to_shape(r::OneTo) = Int(last(r))
6,531✔
UNCOV
841
to_shape(r::AbstractUnitRange) = r
×
842

843
"""
844
    similar(storagetype, axes)
845

846
Create an uninitialized mutable array analogous to that specified by
847
`storagetype`, but with `axes` specified by the last
848
argument.
849

850
**Examples**:
851

852
    similar(Array{Int}, axes(A))
853

854
creates an array that "acts like" an `Array{Int}` (and might indeed be
855
backed by one), but which is indexed identically to `A`. If `A` has
856
conventional indexing, this will be identical to
857
`Array{Int}(undef, size(A))`, but if `A` has unconventional indexing then the
858
indices of the result will match `A`.
859

860
    similar(BitArray, (axes(A, 2),))
861

862
would create a 1-dimensional logical array whose indices match those
863
of the columns of `A`.
864
"""
UNCOV
865
similar(::Type{T}, dims::DimOrInd...) where {T<:AbstractArray} = similar(T, dims)
×
866
similar(::Type{T}, shape::Tuple{Union{Integer, OneTo}, Vararg{Union{Integer, OneTo}}}) where {T<:AbstractArray} = similar(T, to_shape(shape))
1,221✔
867
similar(::Type{T}, dims::Dims) where {T<:AbstractArray} = T(undef, dims)
1,221✔
868

869
"""
870
    empty(v::AbstractVector, [eltype])
871

872
Create an empty vector similar to `v`, optionally changing the `eltype`.
873

874
See also: [`empty!`](@ref), [`isempty`](@ref), [`isassigned`](@ref).
875

876
# Examples
877

878
```jldoctest
879
julia> empty([1.0, 2.0, 3.0])
880
Float64[]
881

882
julia> empty([1.0, 2.0, 3.0], String)
883
String[]
884
```
885
"""
886
empty(a::AbstractVector{T}, ::Type{U}=T) where {T,U} = similar(a, U, 0)
3✔
887

888
# like empty, but should return a mutable collection, a Vector by default
889
emptymutable(a::AbstractVector{T}, ::Type{U}=T) where {T,U} = Vector{U}()
2✔
UNCOV
890
emptymutable(itr, ::Type{U}) where {U} = Vector{U}()
×
891

892
"""
893
    copy!(dst, src) -> dst
894

895
In-place [`copy`](@ref) of `src` into `dst`, discarding any pre-existing
896
elements in `dst`.
897
If `dst` and `src` are of the same type, `dst == src` should hold after
898
the call. If `dst` and `src` are vector types, they must have equal
899
offset. If `dst` and `src` are multidimensional arrays, they must have
900
equal [`axes`](@ref).
901

902
$(_DOCS_ALIASING_WARNING)
903

904
See also [`copyto!`](@ref).
905

906
!!! note
907
    When operating on vector types, if `dst` and `src` are not of the
908
    same length, `dst` is resized to `length(src)` prior to the `copy`.
909

910
!!! compat "Julia 1.1"
911
    This method requires at least Julia 1.1. In Julia 1.0 this method
912
    is available from the `Future` standard library as `Future.copy!`.
913
"""
914
function copy!(dst::AbstractVector, src::AbstractVector)
1✔
915
    firstindex(dst) == firstindex(src) || throw(ArgumentError(
1✔
916
        "vectors must have the same offset for copy! (consider using `copyto!`)"))
917
    if length(dst) != length(src)
1✔
UNCOV
918
        resize!(dst, length(src))
×
919
    end
920
    copyto!(dst, src)
1✔
921
end
922

UNCOV
923
function copy!(dst::AbstractArray, src::AbstractArray)
×
UNCOV
924
    axes(dst) == axes(src) || throw(ArgumentError(
×
925
        "arrays must have the same axes for copy! (consider using `copyto!`)"))
UNCOV
926
    copyto!(dst, src)
×
927
end
928

929
## from general iterable to any array
930

931
# This is `Experimental.@max_methods 1 function copyto! end`, which is not
932
# defined at this point in bootstrap.
933
typeof(function copyto! end).name.max_methods = UInt8(1)
934

935
function copyto!(dest::AbstractArray, src)
12,720✔
936
    destiter = eachindex(dest)
12,720✔
937
    y = iterate(destiter)
12,720✔
938
    for x in src
25,436✔
939
        y === nothing &&
66,149✔
940
            throw(ArgumentError("destination has fewer elements than required"))
941
        dest[y[1]] = x
66,149✔
942
        y = iterate(destiter, y[2])
119,744✔
943
    end
120,387✔
944
    return dest
12,720✔
945
end
946

UNCOV
947
function copyto!(dest::AbstractArray, dstart::Integer, src)
×
UNCOV
948
    i = Int(dstart)
×
UNCOV
949
    if haslength(src) && length(dest) > 0
×
UNCOV
950
        @boundscheck checkbounds(dest, i:(i + length(src) - 1))
×
UNCOV
951
        for x in src
×
UNCOV
952
            @inbounds dest[i] = x
×
UNCOV
953
            i += 1
×
UNCOV
954
        end
×
955
    else
UNCOV
956
        for x in src
×
UNCOV
957
            dest[i] = x
×
UNCOV
958
            i += 1
×
UNCOV
959
        end
×
960
    end
UNCOV
961
    return dest
×
962
end
963

964
# copy from an some iterable object into an AbstractArray
UNCOV
965
function copyto!(dest::AbstractArray, dstart::Integer, src, sstart::Integer)
×
UNCOV
966
    if (sstart < 1)
×
UNCOV
967
        throw(ArgumentError(LazyString("source start offset (",sstart,") is < 1")))
×
968
    end
UNCOV
969
    y = iterate(src)
×
UNCOV
970
    for j = 1:(sstart-1)
×
UNCOV
971
        if y === nothing
×
UNCOV
972
            throw(ArgumentError(LazyString(
×
973
                "source has fewer elements than required, ",
974
                "expected at least ", sstart,", got ", j-1)))
975
        end
UNCOV
976
        y = iterate(src, y[2])
×
UNCOV
977
    end
×
UNCOV
978
    if y === nothing
×
UNCOV
979
        throw(ArgumentError(LazyString(
×
980
            "source has fewer elements than required, ",
981
            "expected at least ",sstart," got ", sstart-1)))
982
    end
UNCOV
983
    i = Int(dstart)
×
UNCOV
984
    while y !== nothing
×
UNCOV
985
        val, st = y
×
UNCOV
986
        dest[i] = val
×
UNCOV
987
        i += 1
×
UNCOV
988
        y = iterate(src, st)
×
UNCOV
989
    end
×
UNCOV
990
    return dest
×
991
end
992

993
# this method must be separate from the above since src might not have a length
UNCOV
994
function copyto!(dest::AbstractArray, dstart::Integer, src, sstart::Integer, n::Integer)
×
UNCOV
995
    n < 0 && throw(ArgumentError(LazyString("tried to copy n=",n,
×
996
        ", elements, but n should be non-negative")))
UNCOV
997
    n == 0 && return dest
×
UNCOV
998
    dmax = dstart + n - 1
×
UNCOV
999
    inds = LinearIndices(dest)
×
UNCOV
1000
    if (dstart ∉ inds || dmax ∉ inds) | (sstart < 1)
×
UNCOV
1001
        sstart < 1 && throw(ArgumentError(LazyString("source start offset (",
×
1002
            sstart,") is < 1")))
UNCOV
1003
        throw(BoundsError(dest, dstart:dmax))
×
1004
    end
UNCOV
1005
    y = iterate(src)
×
UNCOV
1006
    for j = 1:(sstart-1)
×
UNCOV
1007
        if y === nothing
×
UNCOV
1008
            throw(ArgumentError(LazyString(
×
1009
                "source has fewer elements than required, ",
1010
                "expected at least ",sstart,", got ",j-1)))
1011
        end
UNCOV
1012
        y = iterate(src, y[2])
×
UNCOV
1013
    end
×
UNCOV
1014
    if y === nothing
×
1015
        throw(ArgumentError(LazyString(
×
1016
            "source has fewer elements than required, ",
1017
            "expected at least ",sstart," got ", sstart-1)))
1018
    end
UNCOV
1019
    val, st = y
×
UNCOV
1020
    i = Int(dstart)
×
UNCOV
1021
    @inbounds dest[i] = val
×
UNCOV
1022
    for val in Iterators.take(Iterators.rest(src, st), n-1)
×
UNCOV
1023
        i += 1
×
UNCOV
1024
        @inbounds dest[i] = val
×
UNCOV
1025
    end
×
UNCOV
1026
    i < dmax && throw(BoundsError(dest, i))
×
UNCOV
1027
    return dest
×
1028
end
1029

1030
## copy between abstract arrays - generally more efficient
1031
## since a single index variable can be used.
1032

1033
"""
1034
    copyto!(dest::AbstractArray, src) -> dest
1035

1036
Copy all elements from collection `src` to array `dest`, whose length must be greater than
1037
or equal to the length `n` of `src`. The first `n` elements of `dest` are overwritten,
1038
the other elements are left untouched.
1039

1040
See also [`copy!`](@ref Base.copy!), [`copy`](@ref).
1041

1042
$(_DOCS_ALIASING_WARNING)
1043

1044
# Examples
1045
```jldoctest
1046
julia> x = [1., 0., 3., 0., 5.];
1047

1048
julia> y = zeros(7);
1049

1050
julia> copyto!(y, x);
1051

1052
julia> y
1053
7-element Vector{Float64}:
1054
 1.0
1055
 0.0
1056
 3.0
1057
 0.0
1058
 5.0
1059
 0.0
1060
 0.0
1061
```
1062
"""
UNCOV
1063
function copyto!(dest::AbstractArray, src::AbstractArray)
×
1064
    isempty(src) && return dest
8,395✔
UNCOV
1065
    if dest isa BitArray
×
1066
        # avoid ambiguities with other copyto!(::AbstractArray, ::SourceArray) methods
UNCOV
1067
        return _copyto_bitarray!(dest, src)
×
1068
    end
1069
    src′ = unalias(dest, src)
8,395✔
1070
    copyto_unaliased!(IndexStyle(dest), dest, IndexStyle(src′), src′)
8,395✔
1071
end
1072

1073
function copyto!(deststyle::IndexStyle, dest::AbstractArray, srcstyle::IndexStyle, src::AbstractArray)
×
1074
    isempty(src) && return dest
×
1075
    src′ = unalias(dest, src)
×
1076
    copyto_unaliased!(deststyle, dest, srcstyle, src′)
×
1077
end
1078

UNCOV
1079
function copyto_unaliased!(deststyle::IndexStyle, dest::AbstractArray, srcstyle::IndexStyle, src::AbstractArray)
×
1080
    isempty(src) && return dest
1✔
1081
    destinds, srcinds = LinearIndices(dest), LinearIndices(src)
1✔
UNCOV
1082
    idf, isf = first(destinds), first(srcinds)
×
UNCOV
1083
    Δi = idf - isf
×
1084
    (checkbounds(Bool, destinds, isf+Δi) & checkbounds(Bool, destinds, last(srcinds)+Δi)) ||
1✔
1085
        throw(BoundsError(dest, srcinds))
UNCOV
1086
    if deststyle isa IndexLinear
×
UNCOV
1087
        if srcstyle isa IndexLinear
×
1088
            # Single-index implementation
UNCOV
1089
            @inbounds for i in srcinds
×
UNCOV
1090
                dest[i + Δi] = src[i]
×
UNCOV
1091
            end
×
1092
        else
1093
            # Dual-index implementation
UNCOV
1094
            i = idf - 1
×
1095
            @inbounds for a in src
2✔
1096
                dest[i+=1] = a
1✔
1097
            end
1✔
1098
        end
1099
    else
UNCOV
1100
        iterdest, itersrc = eachindex(dest), eachindex(src)
×
UNCOV
1101
        if iterdest == itersrc
×
1102
            # Shared-iterator implementation
UNCOV
1103
            for I in iterdest
×
UNCOV
1104
                @inbounds dest[I] = src[I]
×
UNCOV
1105
            end
×
1106
        else
1107
            # Dual-iterator implementation
UNCOV
1108
            for (Idest, Isrc) in zip(iterdest, itersrc)
×
UNCOV
1109
                @inbounds dest[Idest] = src[Isrc]
×
UNCOV
1110
            end
×
1111
        end
1112
    end
1113
    return dest
1✔
1114
end
1115

UNCOV
1116
function copyto!(dest::AbstractArray, dstart::Integer, src::AbstractArray)
×
UNCOV
1117
    copyto!(dest, dstart, src, first(LinearIndices(src)), length(src))
×
1118
end
1119

UNCOV
1120
function copyto!(dest::AbstractArray, dstart::Integer, src::AbstractArray, sstart::Integer)
×
UNCOV
1121
    srcinds = LinearIndices(src)
×
UNCOV
1122
    checkbounds(Bool, srcinds, sstart) || throw(BoundsError(src, sstart))
×
UNCOV
1123
    copyto!(dest, dstart, src, sstart, last(srcinds)-sstart+1)
×
1124
end
1125

1126
function copyto!(dest::AbstractArray, dstart::Integer,
360,177✔
1127
                 src::AbstractArray, sstart::Integer,
1128
                 n::Integer)
1129
    n == 0 && return dest
360,177✔
1130
    n < 0 && throw(ArgumentError(LazyString("tried to copy n=",
360,177✔
1131
        n," elements, but n should be non-negative")))
1132
    destinds, srcinds = LinearIndices(dest), LinearIndices(src)
360,177✔
1133
    (checkbounds(Bool, destinds, dstart) && checkbounds(Bool, destinds, dstart+n-1)) || throw(BoundsError(dest, dstart:dstart+n-1))
360,177✔
1134
    (checkbounds(Bool, srcinds, sstart)  && checkbounds(Bool, srcinds, sstart+n-1))  || throw(BoundsError(src,  sstart:sstart+n-1))
360,177✔
1135
    src′ = unalias(dest, src)
360,177✔
1136
    @inbounds for i = 0:n-1
360,177✔
1137
        dest[dstart+i] = src′[sstart+i]
20,974,536✔
1138
    end
41,588,895✔
1139
    return dest
360,177✔
1140
end
1141

UNCOV
1142
function copy(a::AbstractArray)
×
UNCOV
1143
    @_propagate_inbounds_meta
×
1144
    copymutable(a)
6,281✔
1145
end
1146

UNCOV
1147
function copyto!(B::AbstractVecOrMat{R}, ir_dest::AbstractRange{Int}, jr_dest::AbstractRange{Int},
×
1148
               A::AbstractVecOrMat{S}, ir_src::AbstractRange{Int}, jr_src::AbstractRange{Int}) where {R,S}
UNCOV
1149
    if length(ir_dest) != length(ir_src)
×
UNCOV
1150
        throw(ArgumentError(LazyString("source and destination must have same size (got ",
×
1151
            length(ir_src)," and ",length(ir_dest),")")))
1152
    end
UNCOV
1153
    if length(jr_dest) != length(jr_src)
×
1154
        throw(ArgumentError(LazyString("source and destination must have same size (got ",
×
1155
            length(jr_src)," and ",length(jr_dest),")")))
1156
    end
UNCOV
1157
    @boundscheck checkbounds(B, ir_dest, jr_dest)
×
UNCOV
1158
    @boundscheck checkbounds(A, ir_src, jr_src)
×
UNCOV
1159
    A′ = unalias(B, A)
×
UNCOV
1160
    jdest = first(jr_dest)
×
UNCOV
1161
    for jsrc in jr_src
×
UNCOV
1162
        idest = first(ir_dest)
×
UNCOV
1163
        for isrc in ir_src
×
UNCOV
1164
            @inbounds B[idest,jdest] = A′[isrc,jsrc]
×
UNCOV
1165
            idest += step(ir_dest)
×
UNCOV
1166
        end
×
UNCOV
1167
        jdest += step(jr_dest)
×
UNCOV
1168
    end
×
UNCOV
1169
    return B
×
1170
end
1171

UNCOV
1172
@noinline _checkaxs(axd, axs) = axd == axs || throw(DimensionMismatch("axes must agree, got $axd and $axs"))
×
1173

1174
function copyto_axcheck!(dest, src)
1✔
1175
    _checkaxs(axes(dest), axes(src))
126✔
1176
    copyto!(dest, src)
127✔
1177
end
1178

1179
"""
1180
    copymutable(a)
1181

1182
Make a mutable copy of an array or iterable `a`.  For `a::Array`,
1183
this is equivalent to `copy(a)`, but for other array types it may
1184
differ depending on the type of `similar(a)`.  For generic iterables
1185
this is equivalent to `collect(a)`.
1186

1187
# Examples
1188
```jldoctest
1189
julia> tup = (1, 2, 3)
1190
(1, 2, 3)
1191

1192
julia> Base.copymutable(tup)
1193
3-element Vector{Int64}:
1194
 1
1195
 2
1196
 3
1197
```
1198
"""
UNCOV
1199
function copymutable(a::AbstractArray)
×
UNCOV
1200
    @_propagate_inbounds_meta
×
1201
    copyto!(similar(a), a)
7,853✔
1202
end
UNCOV
1203
copymutable(itr) = collect(itr)
×
1204

UNCOV
1205
zero(x::AbstractArray{T}) where {T<:Number} = fill!(similar(x, typeof(zero(T))), zero(T))
×
UNCOV
1206
zero(x::AbstractArray{S}) where {S<:Union{Missing, Number}} = fill!(similar(x, typeof(zero(S))), zero(S))
×
UNCOV
1207
zero(x::AbstractArray) = map(zero, x)
×
1208

UNCOV
1209
function _one(unit::T, mat::AbstractMatrix) where {T}
×
UNCOV
1210
    (rows, cols) = axes(mat)
×
UNCOV
1211
    (length(rows) == length(cols)) ||
×
1212
      throw(DimensionMismatch("multiplicative identity defined only for square matrices"))
UNCOV
1213
    zer = zero(unit)::T
×
UNCOV
1214
    require_one_based_indexing(mat)
×
UNCOV
1215
    I = similar(mat, T)
×
UNCOV
1216
    fill!(I, zer)
×
UNCOV
1217
    for i ∈ rows
×
UNCOV
1218
        I[i, i] = unit
×
UNCOV
1219
    end
×
UNCOV
1220
    I
×
1221
end
1222

UNCOV
1223
one(x::AbstractMatrix{T}) where {T} = _one(one(T), x)
×
UNCOV
1224
oneunit(x::AbstractMatrix{T}) where {T} = _one(oneunit(T), x)
×
1225

1226
## iteration support for arrays by iterating over `eachindex` in the array ##
1227
# Allows fast iteration by default for both IndexLinear and IndexCartesian arrays
1228

1229
# While the definitions for IndexLinear are all simple enough to inline on their
1230
# own, IndexCartesian's CartesianIndices is more complicated and requires explicit
1231
# inlining.
1232
function iterate(A::AbstractArray, state=(eachindex(A),))
1233
    y = iterate(state...)
381,827✔
1234
    y === nothing && return nothing
127,952✔
1235
    A[y[1]], (state[1], tail(y)...)
127,805✔
1236
end
1237

1238
isempty(a::AbstractArray) = (length(a) == 0)
1,418,436✔
1239

1240

1241
## range conversions ##
1242

UNCOV
1243
map(::Type{T}, r::StepRange) where {T<:Real} = T(r.start):T(r.step):T(last(r))
×
UNCOV
1244
map(::Type{T}, r::UnitRange) where {T<:Real} = T(r.start):T(last(r))
×
UNCOV
1245
map(::Type{T}, r::StepRangeLen) where {T<:AbstractFloat} = convert(StepRangeLen{T}, r)
×
UNCOV
1246
function map(::Type{T}, r::LinRange) where T<:AbstractFloat
×
UNCOV
1247
    LinRange(T(r.start), T(r.stop), length(r))
×
1248
end
1249

1250
## unsafe/pointer conversions ##
1251

1252
# note: the following type definitions don't mean any AbstractArray is convertible to
1253
# a data Ref. they just map the array element type to the pointer type for
1254
# convenience in cases that work.
1255
pointer(x::AbstractArray{T}) where {T} = unsafe_convert(Ptr{T}, cconvert(Ptr{T}, x))
799,623✔
1256
function pointer(x::AbstractArray{T}, i::Integer) where T
1257
    @inline
32✔
1258
    pointer(x) + Int(_memory_offset(x, i))::Int
344,600✔
1259
end
1260

1261
# The distance from pointer(x) to the element at x[I...] in bytes
1262
_memory_offset(x::DenseArray, I::Vararg{Any,N}) where {N} = (_to_linear_index(x, I...) - first(LinearIndices(x)))*elsize(x)
244,864✔
UNCOV
1263
function _memory_offset(x::AbstractArray, I::Vararg{Any,N}) where {N}
×
UNCOV
1264
    J = _to_subscript_indices(x, I...)
×
UNCOV
1265
    return sum(map((i, s, o)->s*(i-o), J, strides(x), Tuple(first(CartesianIndices(x)))))*elsize(x)
×
1266
end
1267

1268
## Special constprop heuristics for getindex/setindex
1269
typename(typeof(function getindex end)).constprop_heuristic = Core.ARRAY_INDEX_HEURISTIC
1270
typename(typeof(function setindex! end)).constprop_heuristic = Core.ARRAY_INDEX_HEURISTIC
1271

1272
## Approach:
1273
# We only define one fallback method on getindex for all argument types.
1274
# That dispatches to an (inlined) internal _getindex function, where the goal is
1275
# to transform the indices such that we can call the only getindex method that
1276
# we require the type A{T,N} <: AbstractArray{T,N} to define; either:
1277
#       getindex(::A, ::Int) # if IndexStyle(A) == IndexLinear() OR
1278
#       getindex(::A{T,N}, ::Vararg{Int, N}) where {T,N} # if IndexCartesian()
1279
# If the subtype hasn't defined the required method, it falls back to the
1280
# _getindex function again where an error is thrown to prevent stack overflows.
1281
"""
1282
    getindex(A, inds...)
1283

1284
Return a subset of array `A` as selected by the indices `inds`.
1285

1286
Each index may be any [supported index type](@ref man-supported-index-types), such
1287
as an [`Integer`](@ref), [`CartesianIndex`](@ref), [range](@ref Base.AbstractRange), or [array](@ref man-multi-dim-arrays) of supported indices.
1288
A [:](@ref Base.Colon) may be used to select all elements along a specific dimension, and a boolean array (e.g. an `Array{Bool}` or a [`BitArray`](@ref)) may be used to filter for elements where the corresponding index is `true`.
1289

1290
When `inds` selects multiple elements, this function returns a newly
1291
allocated array. To index multiple elements without making a copy,
1292
use [`view`](@ref) instead.
1293

1294
See the manual section on [array indexing](@ref man-array-indexing) for details.
1295

1296
# Examples
1297
```jldoctest
1298
julia> A = [1 2; 3 4]
1299
2×2 Matrix{Int64}:
1300
 1  2
1301
 3  4
1302

1303
julia> getindex(A, 1)
1304
1
1305

1306
julia> getindex(A, [2, 1])
1307
2-element Vector{Int64}:
1308
 3
1309
 1
1310

1311
julia> getindex(A, 2:4)
1312
3-element Vector{Int64}:
1313
 3
1314
 2
1315
 4
1316

1317
julia> getindex(A, 2, 1)
1318
3
1319

1320
julia> getindex(A, CartesianIndex(2, 1))
1321
3
1322

1323
julia> getindex(A, :, 2)
1324
2-element Vector{Int64}:
1325
 2
1326
 4
1327

1328
julia> getindex(A, 2, :)
1329
2-element Vector{Int64}:
1330
 3
1331
 4
1332

1333
julia> getindex(A, A .> 2)
1334
2-element Vector{Int64}:
1335
 3
1336
 4
1337
```
1338
"""
1339
function getindex(A::AbstractArray, I...)
2✔
1340
    @_propagate_inbounds_meta
2✔
1341
    error_if_canonical_getindex(IndexStyle(A), A, I...)
2✔
1342
    _getindex(IndexStyle(A), A, to_indices(A, I)...)
22,008,845✔
1343
end
1344
# To avoid invalidations from multidimensional.jl: getindex(A::Array, i1::Union{Integer, CartesianIndex}, I::Union{Integer, CartesianIndex}...)
1345
@propagate_inbounds getindex(A::Array, i1::Integer, I::Integer...) = A[to_indices(A, (i1, I...))...]
647✔
1346

UNCOV
1347
@inline unsafe_getindex(A::AbstractArray, I...) = @inbounds getindex(A, I...)
×
1348

1349
struct CanonicalIndexError <: Exception
1350
    func::String
1351
    type::Any
UNCOV
1352
    CanonicalIndexError(func::String, @nospecialize(type)) = new(func, type)
×
1353
end
1354

UNCOV
1355
error_if_canonical_getindex(::IndexLinear, A::AbstractArray, ::Int) =
×
1356
    throw(CanonicalIndexError("getindex", typeof(A)))
UNCOV
1357
error_if_canonical_getindex(::IndexCartesian, A::AbstractArray{T,N}, ::Vararg{Int,N}) where {T,N} =
×
1358
    throw(CanonicalIndexError("getindex", typeof(A)))
1359
error_if_canonical_getindex(::IndexStyle, ::AbstractArray, ::Any...) = nothing
2✔
1360

1361
## Internal definitions
1362
_getindex(::IndexStyle, A::AbstractArray, I...) =
×
1363
    error("getindex for $(typeof(A)) with types $(typeof(I)) is not supported")
1364

1365
## IndexLinear Scalar indexing: canonical method is one Int
1366
_getindex(::IndexLinear, A::AbstractVector, i::Int) = (@_propagate_inbounds_meta; getindex(A, i))  # ambiguity resolution in case packages specialize this (to be avoided if at all possible, but see Interpolations.jl)
21,485,647✔
UNCOV
1367
_getindex(::IndexLinear, A::AbstractArray, i::Int) = (@_propagate_inbounds_meta; getindex(A, i))
×
1368
function _getindex(::IndexLinear, A::AbstractArray, I::Vararg{Int,M}) where M
UNCOV
1369
    @inline
×
1370
    @boundscheck checkbounds(A, I...) # generally _to_linear_index requires bounds checking
511,555✔
1371
    @inbounds r = getindex(A, _to_linear_index(A, I...))
511,555✔
UNCOV
1372
    r
×
1373
end
UNCOV
1374
_to_linear_index(A::AbstractArray, i::Integer) = i
×
1375
_to_linear_index(A::AbstractVector, i::Integer, I::Integer...) = i
4✔
UNCOV
1376
_to_linear_index(A::AbstractArray) = first(LinearIndices(A))
×
1377
_to_linear_index(A::AbstractArray, I::Integer...) = (@inline; _sub2ind(A, I...))
621,817✔
1378

1379
## IndexCartesian Scalar indexing: Canonical method is full dimensionality of Ints
UNCOV
1380
function _getindex(::IndexCartesian, A::AbstractArray, I::Vararg{Int,M}) where M
×
UNCOV
1381
    @inline
×
UNCOV
1382
    @boundscheck checkbounds(A, I...) # generally _to_subscript_indices requires bounds checking
×
UNCOV
1383
    @inbounds r = getindex(A, _to_subscript_indices(A, I...)...)
×
UNCOV
1384
    r
×
1385
end
UNCOV
1386
function _getindex(::IndexCartesian, A::AbstractArray{T,N}, I::Vararg{Int, N}) where {T,N}
×
UNCOV
1387
    @_propagate_inbounds_meta
×
UNCOV
1388
    getindex(A, I...)
×
1389
end
UNCOV
1390
_to_subscript_indices(A::AbstractArray, i::Integer) = (@inline; _unsafe_ind2sub(A, i))
×
UNCOV
1391
_to_subscript_indices(A::AbstractArray{T,N}) where {T,N} = (@inline; fill_to_length((), 1, Val(N)))
×
1392
_to_subscript_indices(A::AbstractArray{T,0}) where {T} = ()
×
UNCOV
1393
_to_subscript_indices(A::AbstractArray{T,0}, i::Integer) where {T} = ()
×
1394
_to_subscript_indices(A::AbstractArray{T,0}, I::Integer...) where {T} = ()
×
UNCOV
1395
function _to_subscript_indices(A::AbstractArray{T,N}, I::Integer...) where {T,N}
×
UNCOV
1396
    @inline
×
UNCOV
1397
    J, Jrem = IteratorsMD.split(I, Val(N))
×
UNCOV
1398
    _to_subscript_indices(A, J, Jrem)
×
1399
end
UNCOV
1400
_to_subscript_indices(A::AbstractArray, J::Tuple, Jrem::Tuple{}) =
×
1401
    __to_subscript_indices(A, axes(A), J, Jrem)
UNCOV
1402
function __to_subscript_indices(A::AbstractArray,
×
1403
        ::Tuple{AbstractUnitRange,Vararg{AbstractUnitRange}}, J::Tuple, Jrem::Tuple{})
UNCOV
1404
    @inline
×
UNCOV
1405
    (J..., map(first, tail(_remaining_size(J, axes(A))))...)
×
1406
end
UNCOV
1407
_to_subscript_indices(A, J::Tuple, Jrem::Tuple) = J # already bounds-checked, safe to drop
×
UNCOV
1408
_to_subscript_indices(A::AbstractArray{T,N}, I::Vararg{Int,N}) where {T,N} = I
×
UNCOV
1409
_remaining_size(::Tuple{Any}, t::Tuple) = t
×
UNCOV
1410
_remaining_size(h::Tuple, t::Tuple) = (@inline; _remaining_size(tail(h), tail(t)))
×
1411
_unsafe_ind2sub(::Tuple{}, i) = () # _ind2sub may throw(BoundsError()) in this case
×
UNCOV
1412
_unsafe_ind2sub(sz, i) = (@inline; _ind2sub(sz, i))
×
1413

1414
## Setindex! is defined similarly. We first dispatch to an internal _setindex!
1415
# function that allows dispatch on array storage
1416

1417
"""
1418
    setindex!(A, X, inds...)
1419
    A[inds...] = X
1420

1421
Store values from array `X` within some subset of `A` as specified by `inds`.
1422
The syntax `A[inds...] = X` is equivalent to `(setindex!(A, X, inds...); X)`.
1423

1424
$(_DOCS_ALIASING_WARNING)
1425

1426
# Examples
1427
```jldoctest
1428
julia> A = zeros(2,2);
1429

1430
julia> setindex!(A, [10, 20], [1, 2]);
1431

1432
julia> A[[3, 4]] = [30, 40];
1433

1434
julia> A
1435
2×2 Matrix{Float64}:
1436
 10.0  30.0
1437
 20.0  40.0
1438
```
1439
"""
1440
function setindex!(A::AbstractArray, v, I...)
UNCOV
1441
    @_propagate_inbounds_meta
×
UNCOV
1442
    error_if_canonical_setindex(IndexStyle(A), A, I...)
×
1443
    _setindex!(IndexStyle(A), A, v, to_indices(A, I)...)
621,373✔
1444
end
UNCOV
1445
function unsafe_setindex!(A::AbstractArray, v, I...)
×
UNCOV
1446
    @inline
×
UNCOV
1447
    @inbounds r = setindex!(A, v, I...)
×
UNCOV
1448
    r
×
1449
end
1450

UNCOV
1451
error_if_canonical_setindex(::IndexLinear, A::AbstractArray, ::Int) =
×
1452
    throw(CanonicalIndexError("setindex!", typeof(A)))
UNCOV
1453
error_if_canonical_setindex(::IndexCartesian, A::AbstractArray{T,N}, ::Vararg{Int,N}) where {T,N} =
×
1454
    throw(CanonicalIndexError("setindex!", typeof(A)))
UNCOV
1455
error_if_canonical_setindex(::IndexStyle, ::AbstractArray, ::Any...) = nothing
×
1456

1457
## Internal definitions
1458
_setindex!(::IndexStyle, A::AbstractArray, v, I...) =
×
1459
    error("setindex! for $(typeof(A)) with types $(typeof(I)) is not supported")
1460

1461
## IndexLinear Scalar indexing
1462
_setindex!(::IndexLinear, A::AbstractArray, v, i::Int) = (@_propagate_inbounds_meta; setindex!(A, v, i))
511,111✔
1463
function _setindex!(::IndexLinear, A::AbstractArray, v, I::Vararg{Int,M}) where M
UNCOV
1464
    @inline
×
1465
    @boundscheck checkbounds(A, I...)
110,262✔
1466
    @inbounds r = setindex!(A, v, _to_linear_index(A, I...))
110,262✔
UNCOV
1467
    r
×
1468
end
1469

1470
# IndexCartesian Scalar indexing
UNCOV
1471
function _setindex!(::IndexCartesian, A::AbstractArray{T,N}, v, I::Vararg{Int, N}) where {T,N}
×
UNCOV
1472
    @_propagate_inbounds_meta
×
UNCOV
1473
    setindex!(A, v, I...)
×
1474
end
UNCOV
1475
function _setindex!(::IndexCartesian, A::AbstractArray, v, I::Vararg{Int,M}) where M
×
UNCOV
1476
    @inline
×
UNCOV
1477
    @boundscheck checkbounds(A, I...)
×
UNCOV
1478
    @inbounds r = setindex!(A, v, _to_subscript_indices(A, I...)...)
×
UNCOV
1479
    r
×
1480
end
1481

1482
_unsetindex!(A::AbstractArray, i::Integer) = _unsetindex!(A, to_index(i))
×
1483

1484
"""
1485
    parent(A)
1486

1487
Return the underlying parent object of the view. This parent of objects of types `SubArray`, `SubString`, `ReshapedArray`
1488
or `LinearAlgebra.Transpose` is what was passed as an argument to `view`, `reshape`, `transpose`, etc.
1489
during object creation. If the input is not a wrapped object, return the input itself. If the input is
1490
wrapped multiple times, only the outermost wrapper will be removed.
1491

1492
# Examples
1493
```jldoctest
1494
julia> A = [1 2; 3 4]
1495
2×2 Matrix{Int64}:
1496
 1  2
1497
 3  4
1498

1499
julia> V = view(A, 1:2, :)
1500
2×2 view(::Matrix{Int64}, 1:2, :) with eltype Int64:
1501
 1  2
1502
 3  4
1503

1504
julia> parent(V)
1505
2×2 Matrix{Int64}:
1506
 1  2
1507
 3  4
1508
```
1509
"""
1510
function parent end
1511

UNCOV
1512
parent(a::AbstractArray) = a
×
1513

1514
## rudimentary aliasing detection ##
1515
"""
1516
    Base.unalias(dest, A)
1517

1518
Return either `A` or a copy of `A` in a rough effort to prevent modifications to `dest` from
1519
affecting the returned object. No guarantees are provided.
1520

1521
Custom arrays that wrap or use fields containing arrays that might alias against other
1522
external objects should provide a [`Base.dataids`](@ref) implementation.
1523

1524
This function must return an object of exactly the same type as `A` for performance and type
1525
stability. Mutable custom arrays for which [`copy(A)`](@ref) is not `typeof(A)` should
1526
provide a [`Base.unaliascopy`](@ref) implementation.
1527

1528
See also [`Base.mightalias`](@ref).
1529
"""
1530
unalias(dest, A::AbstractArray) = mightalias(dest, A) ? unaliascopy(A) : A
360,387✔
1531
unalias(dest, A::AbstractRange) = A
1✔
1532
unalias(dest, A) = A
1✔
1533

1534
"""
1535
    Base.unaliascopy(A)
1536

1537
Make a preventative copy of `A` in an operation where `A` [`Base.mightalias`](@ref) against
1538
another array in order to preserve consistent semantics as that other array is mutated.
1539

1540
This must return an object of the same type as `A` to preserve optimal performance in the
1541
much more common case where aliasing does not occur. By default,
1542
`unaliascopy(A::AbstractArray)` will attempt to use [`copy(A)`](@ref), but in cases where
1543
`copy(A)` is not a `typeof(A)`, then the array should provide a custom implementation of
1544
`Base.unaliascopy(A)`.
1545
"""
UNCOV
1546
unaliascopy(A::Array) = copy(A)
×
UNCOV
1547
unaliascopy(A::AbstractArray)::typeof(A) = (@noinline; _unaliascopy(A, copy(A)))
×
UNCOV
1548
_unaliascopy(A::T, C::T) where {T} = C
×
1549
function _unaliascopy(A, C)
×
1550
    Aw = typename(typeof(A)).wrapper
×
1551
    throw(ArgumentError(LazyString("an array of type `", Aw, "` shares memory with another argument ",
×
1552
    "and must make a preventative copy of itself in order to maintain consistent semantics, ",
1553
    "but `copy(::", typeof(A), ")` returns a new array of type `", typeof(C), "`.\n",
1554
    """To fix, implement:
1555
        `Base.unaliascopy(A::""", Aw, ")::typeof(A)`")))
1556
end
1557
unaliascopy(A) = A
×
1558

1559
"""
1560
    Base.mightalias(A::AbstractArray, B::AbstractArray)
1561

1562
Perform a conservative test to check if arrays `A` and `B` might share the same memory.
1563

1564
By default, this simply checks if either of the arrays reference the same memory
1565
regions, as identified by their [`Base.dataids`](@ref).
1566
"""
1567
mightalias(A::AbstractArray, B::AbstractArray) = !isbits(A) && !isbits(B) && !isempty(A) && !isempty(B) && !_isdisjoint(dataids(A), dataids(B))
360,387✔
1568
mightalias(x, y) = false
×
1569

1570
_isdisjoint(as::Tuple{}, bs::Tuple{}) = true
×
1571
_isdisjoint(as::Tuple{}, bs::Tuple{UInt}) = true
×
1572
_isdisjoint(as::Tuple{}, bs::Tuple) = true
×
1573
_isdisjoint(as::Tuple{UInt}, bs::Tuple{}) = true
×
1574
_isdisjoint(as::Tuple{UInt}, bs::Tuple{UInt}) = as[1] != bs[1]
360,386✔
UNCOV
1575
_isdisjoint(as::Tuple{UInt}, bs::Tuple) = !(as[1] in bs)
×
1576
_isdisjoint(as::Tuple, bs::Tuple{}) = true
×
UNCOV
1577
_isdisjoint(as::Tuple, bs::Tuple{UInt}) = !(bs[1] in as)
×
UNCOV
1578
_isdisjoint(as::Tuple, bs::Tuple) = !(as[1] in bs) && _isdisjoint(tail(as), bs)
×
1579

1580
"""
1581
    Base.dataids(A::AbstractArray)
1582

1583
Return a tuple of `UInt`s that represent the mutable data segments of an array.
1584

1585
Custom arrays that would like to opt-in to aliasing detection of their component
1586
parts can specialize this method to return the concatenation of the `dataids` of
1587
their component parts.  A typical definition for an array that wraps a parent is
1588
`Base.dataids(C::CustomArray) = dataids(C.parent)`.
1589
"""
1590
dataids(A::AbstractArray) = (UInt(objectid(A)),)
35✔
1591
dataids(A::Memory) = (UInt(A.ptr),)
720,737✔
1592
dataids(A::Array) = dataids(A.ref.mem)
720,564✔
UNCOV
1593
dataids(::AbstractRange) = ()
×
UNCOV
1594
dataids(x) = ()
×
1595

1596
## get (getindex with a default value) ##
1597

1598
RangeVecIntList{A<:AbstractVector{Int}} = Union{Tuple{Vararg{Union{AbstractRange, AbstractVector{Int}}}},
1599
    AbstractVector{UnitRange{Int}}, AbstractVector{AbstractRange{Int}}, AbstractVector{A}}
1600

UNCOV
1601
get(A::AbstractArray, i::Integer, default) = checkbounds(Bool, A, i) ? A[i] : default
×
UNCOV
1602
get(A::AbstractArray, I::Tuple{}, default) = checkbounds(Bool, A) ? A[] : default
×
UNCOV
1603
get(A::AbstractArray, I::Dims, default) = checkbounds(Bool, A, I...) ? A[I...] : default
×
UNCOV
1604
get(f::Callable, A::AbstractArray, i::Integer) = checkbounds(Bool, A, i) ? A[i] : f()
×
UNCOV
1605
get(f::Callable, A::AbstractArray, I::Tuple{}) = checkbounds(Bool, A) ? A[] : f()
×
UNCOV
1606
get(f::Callable, A::AbstractArray, I::Dims) = checkbounds(Bool, A, I...) ? A[I...] : f()
×
1607

1608
function get!(X::AbstractVector{T}, A::AbstractVector, I::Union{AbstractRange,AbstractVector{Int}}, default::T) where T
×
1609
    # 1d is not linear indexing
1610
    ind = findall(in(axes1(A)), I)
×
1611
    X[ind] = A[I[ind]]
×
1612
    Xind = axes1(X)
×
1613
    X[first(Xind):first(ind)-1] = default
×
1614
    X[last(ind)+1:last(Xind)] = default
×
1615
    X
×
1616
end
UNCOV
1617
function get!(X::AbstractArray{T}, A::AbstractArray, I::Union{AbstractRange,AbstractVector{Int}}, default::T) where T
×
1618
    # Linear indexing
UNCOV
1619
    ind = findall(in(1:length(A)), I)
×
UNCOV
1620
    X[ind] = A[I[ind]]
×
UNCOV
1621
    fill!(view(X, 1:first(ind)-1), default)
×
UNCOV
1622
    fill!(view(X, last(ind)+1:length(X)), default)
×
UNCOV
1623
    X
×
1624
end
1625

UNCOV
1626
get(A::AbstractArray, I::AbstractRange, default) = get!(similar(A, typeof(default), index_shape(I)), A, I, default)
×
1627

UNCOV
1628
function get!(X::AbstractArray{T}, A::AbstractArray, I::RangeVecIntList, default::T) where T
×
UNCOV
1629
    fill!(X, default)
×
UNCOV
1630
    dst, src = indcopy(size(A), I)
×
UNCOV
1631
    X[dst...] = A[src...]
×
UNCOV
1632
    X
×
1633
end
1634

UNCOV
1635
get(A::AbstractArray, I::RangeVecIntList, default) =
×
1636
    get!(similar(A, typeof(default), index_shape(I...)), A, I, default)
1637

1638
## structured matrix methods ##
UNCOV
1639
replace_in_print_matrix(A::AbstractMatrix,i::Integer,j::Integer,s::AbstractString) = s
×
UNCOV
1640
replace_in_print_matrix(A::AbstractVector,i::Integer,j::Integer,s::AbstractString) = s
×
1641

1642
## Concatenation ##
1643
eltypeof(x) = typeof(x)
1✔
1644
eltypeof(x::AbstractArray) = eltype(x)
3✔
1645

1646
promote_eltypeof() = error()
×
1647
promote_eltypeof(v1) = eltypeof(v1)
×
1648
promote_eltypeof(v1, v2) = promote_type(eltypeof(v1), eltypeof(v2))
2✔
UNCOV
1649
promote_eltypeof(v1, v2, vs...) = (@inline; afoldl(((::Type{T}, y) where {T}) -> promote_type(T, eltypeof(y)), promote_eltypeof(v1, v2), vs...))
×
UNCOV
1650
promote_eltypeof(v1::T, vs::T...) where {T} = eltypeof(v1)
×
UNCOV
1651
promote_eltypeof(v1::AbstractArray{T}, vs::AbstractArray{T}...) where {T} = T
×
1652

1653
promote_eltype() = error()
×
1654
promote_eltype(v1) = eltype(v1)
×
UNCOV
1655
promote_eltype(v1, v2) = promote_type(eltype(v1), eltype(v2))
×
UNCOV
1656
promote_eltype(v1, v2, vs...) = (@inline; afoldl(((::Type{T}, y) where {T}) -> promote_type(T, eltype(y)), promote_eltype(v1, v2), vs...))
×
UNCOV
1657
promote_eltype(v1::T, vs::T...) where {T} = eltype(T)
×
1658
promote_eltype(v1::AbstractArray{T}, vs::AbstractArray{T}...) where {T} = T
6✔
1659

1660
#TODO: ERROR CHECK
UNCOV
1661
_cat(catdim::Int) = Vector{Any}()
×
1662

UNCOV
1663
typed_vcat(::Type{T}) where {T} = Vector{T}()
×
UNCOV
1664
typed_hcat(::Type{T}) where {T} = Vector{T}()
×
1665

1666
## cat: special cases
UNCOV
1667
vcat(X::T...) where {T}         = T[ X[i] for i=eachindex(X) ]
×
UNCOV
1668
vcat(X::T...) where {T<:Number} = T[ X[i] for i=eachindex(X) ]
×
UNCOV
1669
hcat(X::T...) where {T}         = T[ X[j] for i=1:1, j=eachindex(X) ]
×
UNCOV
1670
hcat(X::T...) where {T<:Number} = T[ X[j] for i=1:1, j=eachindex(X) ]
×
1671

UNCOV
1672
vcat(X::Number...) = hvcat_fill!(Vector{promote_typeof(X...)}(undef, length(X)), X)
×
UNCOV
1673
hcat(X::Number...) = hvcat_fill!(Matrix{promote_typeof(X...)}(undef, 1,length(X)), X)
×
UNCOV
1674
typed_vcat(::Type{T}, X::Number...) where {T} = hvcat_fill!(Vector{T}(undef, length(X)), X)
×
UNCOV
1675
typed_hcat(::Type{T}, X::Number...) where {T} = hvcat_fill!(Matrix{T}(undef, 1,length(X)), X)
×
1676

UNCOV
1677
vcat(V::AbstractVector...) = typed_vcat(promote_eltype(V...), V...)
×
UNCOV
1678
vcat(V::AbstractVector{T}...) where {T} = typed_vcat(T, V...)
×
1679

1680
# FIXME: this alias would better be Union{AbstractVector{T}, Tuple{Vararg{T}}}
1681
# and method signatures should do AbstractVecOrTuple{<:T} when they want covariance,
1682
# but that solution currently fails (see #27188 and #27224)
1683
AbstractVecOrTuple{T} = Union{AbstractVector{<:T}, Tuple{Vararg{T}}}
1684

1685
_typed_vcat_similar(V, ::Type{T}, n) where T = similar(V[1], T, n)
2✔
1686
_typed_vcat(::Type{T}, V::AbstractVecOrTuple{AbstractVector}) where T =
3✔
1687
    _typed_vcat!(_typed_vcat_similar(V, T, sum(map(length, V))), V)
1688

1689
function _typed_vcat!(a::AbstractVector{T}, V::AbstractVecOrTuple{AbstractVector}) where T
1690
    pos = 1
1✔
1691
    for k=1:Int(length(V))::Int
2✔
1692
        Vk = V[k]
80✔
1693
        p1 = pos + Int(length(Vk))::Int - 1
80✔
1694
        a[pos:p1] = Vk
99✔
1695
        pos = p1+1
80✔
1696
    end
81✔
1697
    a
1✔
1698
end
1699

UNCOV
1700
typed_hcat(::Type{T}, A::AbstractVecOrMat...) where {T} = _typed_hcat(T, A)
×
1701

1702
# Catch indexing errors like v[i +1] (instead of v[i+1] or v[i + 1]), where indexing is
1703
# interpreted as a typed concatenation. (issue #49676)
UNCOV
1704
typed_hcat(::AbstractArray, other...) = throw(ArgumentError("It is unclear whether you \
×
1705
    intend to perform an indexing operation or typed concatenation. If you intend to \
1706
    perform indexing (v[1 + 2]), adjust spacing or insert missing operator to clarify. \
1707
    If you intend to perform typed concatenation (T[1 2]), ensure that T is a type."))
1708

1709

UNCOV
1710
hcat(A::AbstractVecOrMat...) = typed_hcat(promote_eltype(A...), A...)
×
UNCOV
1711
hcat(A::AbstractVecOrMat{T}...) where {T} = typed_hcat(T, A...)
×
1712

UNCOV
1713
function _typed_hcat(::Type{T}, A::AbstractVecOrTuple{AbstractVecOrMat}) where T
×
UNCOV
1714
    nargs = length(A)
×
UNCOV
1715
    nrows = size(A[1], 1)
×
UNCOV
1716
    ncols = 0
×
UNCOV
1717
    dense = true
×
UNCOV
1718
    for j = 1:nargs
×
UNCOV
1719
        Aj = A[j]
×
UNCOV
1720
        if size(Aj, 1) != nrows
×
UNCOV
1721
            throw(DimensionMismatch("number of rows of each array must match (got $(map(x->size(x,1), A)))"))
×
1722
        end
UNCOV
1723
        dense &= isa(Aj,Array)
×
UNCOV
1724
        nd = ndims(Aj)
×
UNCOV
1725
        ncols += (nd==2 ? size(Aj,2) : 1)
×
UNCOV
1726
    end
×
UNCOV
1727
    B = similar(A[1], T, nrows, ncols)
×
UNCOV
1728
    pos = 1
×
UNCOV
1729
    if dense
×
UNCOV
1730
        for k=1:nargs
×
UNCOV
1731
            Ak = A[k]
×
UNCOV
1732
            n = length(Ak)
×
UNCOV
1733
            copyto!(B, pos, Ak, 1, n)
×
UNCOV
1734
            pos += n
×
UNCOV
1735
        end
×
1736
    else
UNCOV
1737
        for k=1:nargs
×
UNCOV
1738
            Ak = A[k]
×
UNCOV
1739
            p1 = pos+(isa(Ak,AbstractMatrix) ? size(Ak, 2) : 1)-1
×
UNCOV
1740
            B[:, pos:p1] = Ak
×
UNCOV
1741
            pos = p1+1
×
UNCOV
1742
        end
×
1743
    end
UNCOV
1744
    return B
×
1745
end
1746

UNCOV
1747
vcat(A::AbstractVecOrMat...) = typed_vcat(promote_eltype(A...), A...)
×
UNCOV
1748
vcat(A::AbstractVecOrMat{T}...) where {T} = typed_vcat(T, A...)
×
1749

UNCOV
1750
function _typed_vcat(::Type{T}, A::AbstractVecOrTuple{AbstractVecOrMat}) where T
×
UNCOV
1751
    nargs = length(A)
×
UNCOV
1752
    nrows = sum(a->size(a, 1), A)::Int
×
UNCOV
1753
    ncols = size(A[1], 2)
×
UNCOV
1754
    for j = 2:nargs
×
UNCOV
1755
        if size(A[j], 2) != ncols
×
UNCOV
1756
            throw(DimensionMismatch("number of columns of each array must match (got $(map(x->size(x,2), A)))"))
×
1757
        end
UNCOV
1758
    end
×
UNCOV
1759
    B = similar(A[1], T, nrows, ncols)
×
UNCOV
1760
    pos = 1
×
UNCOV
1761
    for k=1:nargs
×
UNCOV
1762
        Ak = A[k]
×
UNCOV
1763
        p1 = pos+size(Ak,1)::Int-1
×
UNCOV
1764
        B[pos:p1, :] = Ak
×
UNCOV
1765
        pos = p1+1
×
UNCOV
1766
    end
×
UNCOV
1767
    return B
×
1768
end
1769

1770
typed_vcat(::Type{T}, A::AbstractVecOrMat...) where {T} = _typed_vcat(T, A)
2✔
1771

1772
reduce(::typeof(vcat), A::AbstractVector{<:AbstractVecOrMat}) =
1✔
1773
    _typed_vcat(mapreduce(eltype, promote_type, A), A)
1774

UNCOV
1775
reduce(::typeof(hcat), A::AbstractVector{<:AbstractVecOrMat}) =
×
1776
    _typed_hcat(mapreduce(eltype, promote_type, A), A)
1777

1778
## cat: general case
1779

1780
# helper functions
1781
cat_size(A) = (1,)
1✔
1782
cat_size(A::AbstractArray) = size(A)
3✔
1783
cat_size(A, d) = 1
×
1784
cat_size(A::AbstractArray, d) = size(A, d)
3✔
1785

1786
cat_length(::Any) = 1
×
UNCOV
1787
cat_length(a::AbstractArray) = length(a)
×
1788

1789
cat_ndims(a) = 0
×
UNCOV
1790
cat_ndims(a::AbstractArray) = ndims(a)
×
1791

1792
cat_indices(A, d) = OneTo(1)
1✔
1793
cat_indices(A::AbstractArray, d) = axes(A, d)
3✔
1794

1795
cat_similar(A, ::Type{T}, shape::Tuple) where T = Array{T}(undef, shape)
1✔
1796
cat_similar(A, ::Type{T}, shape::Vector) where T = Array{T}(undef, shape...)
×
1797
cat_similar(A::Array, ::Type{T}, shape::Tuple) where T = Array{T}(undef, shape)
1✔
1798
cat_similar(A::Array, ::Type{T}, shape::Vector) where T = Array{T}(undef, shape...)
×
UNCOV
1799
cat_similar(A::AbstractArray, T::Type, shape::Tuple) = similar(A, T, shape)
×
1800
cat_similar(A::AbstractArray, T::Type, shape::Vector) = similar(A, T, shape...)
×
1801

1802
# These are for backwards compatibility (even though internal)
1803
cat_shape(dims, shape::Tuple{Vararg{Int}}) = shape
×
UNCOV
1804
function cat_shape(dims, shapes::Tuple)
×
UNCOV
1805
    out_shape = ()
×
UNCOV
1806
    for s in shapes
×
UNCOV
1807
        out_shape = _cshp(1, dims, out_shape, s)
×
UNCOV
1808
    end
×
UNCOV
1809
    return out_shape
×
1810
end
1811
# The new way to compute the shape (more inferable than combining cat_size & cat_shape, due to Varargs + issue#36454)
1812
cat_size_shape(dims) = ntuple(zero, Val(length(dims)))
×
1813
@inline cat_size_shape(dims, X, tail...) = _cat_size_shape(dims, _cshp(1, dims, (), cat_size(X)), tail...)
2✔
UNCOV
1814
_cat_size_shape(dims, shape) = shape
×
1815
@inline _cat_size_shape(dims, shape, X, tail...) = _cat_size_shape(dims, _cshp(1, dims, shape, cat_size(X)), tail...)
2✔
1816

1817
_cshp(ndim::Int, ::Tuple{}, ::Tuple{}, ::Tuple{}) = ()
×
UNCOV
1818
_cshp(ndim::Int, ::Tuple{}, ::Tuple{}, nshape) = nshape
×
UNCOV
1819
_cshp(ndim::Int, dims, ::Tuple{}, ::Tuple{}) = ntuple(Returns(1), Val(length(dims)))
×
UNCOV
1820
@inline _cshp(ndim::Int, dims, shape, ::Tuple{}) =
×
1821
    (shape[1] + dims[1], _cshp(ndim + 1, tail(dims), tail(shape), ())...)
1822
@inline _cshp(ndim::Int, dims, ::Tuple{}, nshape) =
2✔
1823
    (nshape[1], _cshp(ndim + 1, tail(dims), (), tail(nshape))...)
UNCOV
1824
@inline function _cshp(ndim::Int, ::Tuple{}, shape, ::Tuple{})
×
UNCOV
1825
    _cs(ndim, shape[1], 1)
×
UNCOV
1826
    (1, _cshp(ndim + 1, (), tail(shape), ())...)
×
1827
end
UNCOV
1828
@inline function _cshp(ndim::Int, ::Tuple{}, shape, nshape)
×
UNCOV
1829
    next = _cs(ndim, shape[1], nshape[1])
×
UNCOV
1830
    (next, _cshp(ndim + 1, (), tail(shape), tail(nshape))...)
×
1831
end
1832
@inline function _cshp(ndim::Int, dims, shape, nshape)
1833
    a = shape[1]
2✔
1834
    b = nshape[1]
2✔
1835
    next = dims[1] ? a + b : _cs(ndim, a, b)
2✔
1836
    (next, _cshp(ndim + 1, tail(dims), tail(shape), tail(nshape))...)
2✔
1837
end
1838

UNCOV
1839
_cs(d, a, b) = (a == b ? a : throw(DimensionMismatch(
×
1840
    "mismatch in dimension $d (expected $a got $b)")))
1841

UNCOV
1842
dims2cat(::Val{dims}) where dims = dims2cat(dims)
×
UNCOV
1843
function dims2cat(dims)
×
UNCOV
1844
    if any(≤(0), dims)
×
UNCOV
1845
        throw(ArgumentError("All cat dimensions must be positive integers, but got $dims"))
×
1846
    end
UNCOV
1847
    ntuple(in(dims), maximum(dims))
×
1848
end
1849

1850
_cat(dims, X...) = _cat_t(dims, promote_eltypeof(X...), X...)
3✔
1851

1852
@inline function _cat_t(dims, ::Type{T}, X...) where {T}
1853
    catdims = dims2cat(dims)
2✔
1854
    shape = cat_size_shape(catdims, X...)
2✔
1855
    A = cat_similar(X[1], T, shape)
2✔
1856
    if count(!iszero, catdims)::Int > 1
2✔
UNCOV
1857
        fill!(A, zero(T))
×
1858
    end
1859
    return __cat(A, shape, catdims, X...)
3✔
1860
end
1861
# this version of `cat_t` is not very kind for inference and so its usage should be avoided,
1862
# nevertheless it is here just for compat after https://github.com/JuliaLang/julia/pull/45028
1863
@inline cat_t(::Type{T}, X...; dims) where {T} = _cat_t(dims, T, X...)
×
1864

1865
# Why isn't this called `__cat!`?
1866
__cat(A, shape, catdims, X...) = __cat_offset!(A, shape, catdims, ntuple(zero, length(shape)), X...)
3✔
1867

1868
function __cat_offset!(A, shape, catdims, offsets, x, X...)
1✔
1869
    # splitting the "work" on x from X... may reduce latency (fewer costly specializations)
1870
    newoffsets = __cat_offset1!(A, shape, catdims, offsets, x)
5✔
1871
    return __cat_offset!(A, shape, catdims, newoffsets, X...)
5✔
1872
end
1873
__cat_offset!(A, shape, catdims, offsets) = A
2✔
1874

1875
function __cat_offset1!(A, shape, catdims, offsets, x)
1876
    inds = ntuple(length(offsets)) do i
5✔
1877
        (i <= length(catdims) && catdims[i]) ? offsets[i] .+ cat_indices(x, i) : 1:shape[i]
5✔
1878
    end
1879
    _copy_or_fill!(A, inds, x)
6✔
1880
    newoffsets = ntuple(length(offsets)) do i
4✔
1881
        (i <= length(catdims) && catdims[i]) ? offsets[i] + cat_size(x, i) : offsets[i]
4✔
1882
    end
1883
    return newoffsets
4✔
1884
end
1885

1886
_copy_or_fill!(A, inds, x) = fill!(view(A, inds...), x)
1✔
1887
_copy_or_fill!(A, inds, x::AbstractArray) = (A[inds...] = x)
5✔
1888

1889
"""
1890
    vcat(A...)
1891

1892
Concatenate arrays or numbers vertically. Equivalent to [`cat`](@ref)`(A...; dims=1)`,
1893
and to the syntax `[a; b; c]`.
1894

1895
To concatenate a large vector of arrays, `reduce(vcat, A)` calls an efficient method
1896
when `A isa AbstractVector{<:AbstractVecOrMat}`, rather than working pairwise.
1897

1898
See also [`hcat`](@ref), [`Iterators.flatten`](@ref), [`stack`](@ref).
1899

1900
# Examples
1901
```jldoctest
1902
julia> v = vcat([1,2], [3,4])
1903
4-element Vector{Int64}:
1904
 1
1905
 2
1906
 3
1907
 4
1908

1909
julia> v == vcat(1, 2, [3,4])  # accepts numbers
1910
true
1911

1912
julia> v == [1; 2; [3,4]]  # syntax for the same operation
1913
true
1914

1915
julia> summary(ComplexF64[1; 2; [3,4]])  # syntax for supplying the element type
1916
"4-element Vector{ComplexF64}"
1917

1918
julia> vcat(range(1, 2, length=3))  # collects lazy ranges
1919
3-element Vector{Float64}:
1920
 1.0
1921
 1.5
1922
 2.0
1923

1924
julia> two = ([10, 20, 30]', Float64[4 5 6; 7 8 9])  # row vector and a matrix
1925
(adjoint([10, 20, 30]), [4.0 5.0 6.0; 7.0 8.0 9.0])
1926

1927
julia> vcat(two...)
1928
3×3 Matrix{Float64}:
1929
 10.0  20.0  30.0
1930
  4.0   5.0   6.0
1931
  7.0   8.0   9.0
1932

1933
julia> vs = [[1, 2], [3, 4], [5, 6]];
1934

1935
julia> reduce(vcat, vs)  # more efficient than vcat(vs...)
1936
6-element Vector{Int64}:
1937
 1
1938
 2
1939
 3
1940
 4
1941
 5
1942
 6
1943

1944
julia> ans == collect(Iterators.flatten(vs))
1945
true
1946
```
1947
"""
UNCOV
1948
vcat(X...) = cat(X...; dims=Val(1))
×
1949
"""
1950
    hcat(A...)
1951

1952
Concatenate arrays or numbers horizontally. Equivalent to [`cat`](@ref)`(A...; dims=2)`,
1953
and to the syntax `[a b c]` or `[a;; b;; c]`.
1954

1955
For a large vector of arrays, `reduce(hcat, A)` calls an efficient method
1956
when `A isa AbstractVector{<:AbstractVecOrMat}`.
1957
For a vector of vectors, this can also be written [`stack`](@ref)`(A)`.
1958

1959
See also [`vcat`](@ref), [`hvcat`](@ref).
1960

1961
# Examples
1962
```jldoctest
1963
julia> hcat([1,2], [3,4], [5,6])
1964
2×3 Matrix{Int64}:
1965
 1  3  5
1966
 2  4  6
1967

1968
julia> hcat(1, 2, [30 40], [5, 6, 7]')  # accepts numbers
1969
1×7 Matrix{Int64}:
1970
 1  2  30  40  5  6  7
1971

1972
julia> ans == [1 2 [30 40] [5, 6, 7]']  # syntax for the same operation
1973
true
1974

1975
julia> Float32[1 2 [30 40] [5, 6, 7]']  # syntax for supplying the eltype
1976
1×7 Matrix{Float32}:
1977
 1.0  2.0  30.0  40.0  5.0  6.0  7.0
1978

1979
julia> ms = [zeros(2,2), [1 2; 3 4], [50 60; 70 80]];
1980

1981
julia> reduce(hcat, ms)  # more efficient than hcat(ms...)
1982
2×6 Matrix{Float64}:
1983
 0.0  0.0  1.0  2.0  50.0  60.0
1984
 0.0  0.0  3.0  4.0  70.0  80.0
1985

1986
julia> stack(ms) |> summary  # disagrees on a vector of matrices
1987
"2×2×3 Array{Float64, 3}"
1988

1989
julia> hcat(Int[], Int[], Int[])  # empty vectors, each of size (0,)
1990
0×3 Matrix{Int64}
1991

1992
julia> hcat([1.1, 9.9], Matrix(undef, 2, 0))  # hcat with empty 2×0 Matrix
1993
2×1 Matrix{Any}:
1994
 1.1
1995
 9.9
1996
```
1997
"""
UNCOV
1998
hcat(X...) = cat(X...; dims=Val(2))
×
1999

UNCOV
2000
typed_vcat(::Type{T}, X...) where T = _cat_t(Val(1), T, X...)
×
UNCOV
2001
typed_hcat(::Type{T}, X...) where T = _cat_t(Val(2), T, X...)
×
2002

2003
"""
2004
    cat(A...; dims)
2005

2006
Concatenate the input arrays along the dimensions specified in `dims`.
2007

2008
Along a dimension `d in dims`, the size of the output array is `sum(size(a,d) for
2009
a in A)`.
2010
Along other dimensions, all input arrays should have the same size,
2011
which will also be the size of the output array along those dimensions.
2012

2013
If `dims` is a single number, the different arrays are tightly packed along that dimension.
2014
If `dims` is an iterable containing several dimensions, the positions along these dimensions
2015
are increased simultaneously for each input array, filling with zero elsewhere.
2016
This allows one to construct block-diagonal matrices as `cat(matrices...; dims=(1,2))`,
2017
and their higher-dimensional analogues.
2018

2019
The special case `dims=1` is [`vcat`](@ref), and `dims=2` is [`hcat`](@ref).
2020
See also [`hvcat`](@ref), [`hvncat`](@ref), [`stack`](@ref), [`repeat`](@ref).
2021

2022
The keyword also accepts `Val(dims)`.
2023

2024
!!! compat "Julia 1.8"
2025
    For multiple dimensions `dims = Val(::Tuple)` was added in Julia 1.8.
2026

2027
# Examples
2028

2029
Concatenate two arrays in different dimensions:
2030
```jldoctest
2031
julia> a = [1 2 3]
2032
1×3 Matrix{Int64}:
2033
 1  2  3
2034

2035
julia> b = [4 5 6]
2036
1×3 Matrix{Int64}:
2037
 4  5  6
2038

2039
julia> cat(a, b; dims=1)
2040
2×3 Matrix{Int64}:
2041
 1  2  3
2042
 4  5  6
2043

2044
julia> cat(a, b; dims=2)
2045
1×6 Matrix{Int64}:
2046
 1  2  3  4  5  6
2047

2048
julia> cat(a, b; dims=(1, 2))
2049
2×6 Matrix{Int64}:
2050
 1  2  3  0  0  0
2051
 0  0  0  4  5  6
2052
```
2053

2054
# Extended Help
2055

2056
Concatenate 3D arrays:
2057
```jldoctest
2058
julia> a = ones(2, 2, 3);
2059

2060
julia> b = ones(2, 2, 4);
2061

2062
julia> c = cat(a, b; dims=3);
2063

2064
julia> size(c) == (2, 2, 7)
2065
true
2066
```
2067

2068
Concatenate arrays of different sizes:
2069
```jldoctest
2070
julia> cat([1 2; 3 4], [pi, pi], fill(10, 2,3,1); dims=2)  # same as hcat
2071
2×6×1 Array{Float64, 3}:
2072
[:, :, 1] =
2073
 1.0  2.0  3.14159  10.0  10.0  10.0
2074
 3.0  4.0  3.14159  10.0  10.0  10.0
2075
```
2076

2077
Construct a block diagonal matrix:
2078
```
2079
julia> cat(true, trues(2,2), trues(4)', dims=(1,2))  # block-diagonal
2080
4×7 Matrix{Bool}:
2081
 1  0  0  0  0  0  0
2082
 0  1  1  0  0  0  0
2083
 0  1  1  0  0  0  0
2084
 0  0  0  1  1  1  1
2085
```
2086

2087
```
2088
julia> cat(1, [2], [3;;]; dims=Val(2))
2089
1×3 Matrix{Int64}:
2090
 1  2  3
2091
```
2092

2093
!!! note
2094
    `cat` does not join two strings, you may want to use `*`.
2095

2096
```jldoctest
2097
julia> a = "aaa";
2098

2099
julia> b = "bbb";
2100

2101
julia> cat(a, b; dims=1)
2102
2-element Vector{String}:
2103
 "aaa"
2104
 "bbb"
2105

2106
julia> cat(a, b; dims=2)
2107
1×2 Matrix{String}:
2108
 "aaa"  "bbb"
2109

2110
julia> a * b
2111
"aaabbb"
2112
```
2113
"""
2114
@inline cat(A...; dims) = _cat(dims, A...)
5✔
2115
# `@constprop :aggressive` allows `catdims` to be propagated as constant improving return type inference
UNCOV
2116
@constprop :aggressive _cat(catdims, A::AbstractArray{T}...) where {T} = _cat_t(catdims, T, A...)
×
2117

2118
# The specializations for 1 and 2 inputs are important
2119
# especially when running with --inline=no, see #11158
UNCOV
2120
vcat(A::AbstractArray) = cat(A; dims=Val(1))
×
UNCOV
2121
vcat(A::AbstractArray, B::AbstractArray) = cat(A, B; dims=Val(1))
×
2122
vcat(A::AbstractArray...) = cat(A...; dims=Val(1))
×
2123
vcat(A::Union{AbstractArray,Number}...) = cat(A...; dims=Val(1))
1✔
UNCOV
2124
hcat(A::AbstractArray) = cat(A; dims=Val(2))
×
UNCOV
2125
hcat(A::AbstractArray, B::AbstractArray) = cat(A, B; dims=Val(2))
×
UNCOV
2126
hcat(A::AbstractArray...) = cat(A...; dims=Val(2))
×
UNCOV
2127
hcat(A::Union{AbstractArray,Number}...) = cat(A...; dims=Val(2))
×
2128

UNCOV
2129
typed_vcat(T::Type, A::AbstractArray) = _cat_t(Val(1), T, A)
×
UNCOV
2130
typed_vcat(T::Type, A::AbstractArray, B::AbstractArray) = _cat_t(Val(1), T, A, B)
×
UNCOV
2131
typed_vcat(T::Type, A::AbstractArray...) = _cat_t(Val(1), T, A...)
×
UNCOV
2132
typed_hcat(T::Type, A::AbstractArray) = _cat_t(Val(2), T, A)
×
UNCOV
2133
typed_hcat(T::Type, A::AbstractArray, B::AbstractArray) = _cat_t(Val(2), T, A, B)
×
UNCOV
2134
typed_hcat(T::Type, A::AbstractArray...) = _cat_t(Val(2), T, A...)
×
2135

2136
# 2d horizontal and vertical concatenation
2137

2138
# these are produced in lowering if splatting occurs inside hvcat
UNCOV
2139
hvcat_rows(rows::Tuple...) = hvcat(map(length, rows), (rows...)...)
×
UNCOV
2140
typed_hvcat_rows(T::Type, rows::Tuple...) = typed_hvcat(T, map(length, rows), (rows...)...)
×
2141

UNCOV
2142
function hvcat(nbc::Int, as...)
×
2143
    # nbc = # of block columns
UNCOV
2144
    n = length(as)
×
UNCOV
2145
    mod(n,nbc) != 0 &&
×
2146
        throw(ArgumentError("number of arrays $n is not a multiple of the requested number of block columns $nbc"))
UNCOV
2147
    nbr = div(n,nbc)
×
UNCOV
2148
    hvcat(ntuple(Returns(nbc), nbr), as...)
×
2149
end
2150

2151
"""
2152
    hvcat(blocks_per_row::Union{Tuple{Vararg{Int}}, Int}, values...)
2153

2154
Horizontal and vertical concatenation in one call. This function is called for block matrix
2155
syntax. The first argument specifies the number of arguments to concatenate in each block
2156
row. If the first argument is a single integer `n`, then all block rows are assumed to have `n`
2157
block columns.
2158

2159
# Examples
2160
```jldoctest
2161
julia> a, b, c, d, e, f = 1, 2, 3, 4, 5, 6
2162
(1, 2, 3, 4, 5, 6)
2163

2164
julia> [a b c; d e f]
2165
2×3 Matrix{Int64}:
2166
 1  2  3
2167
 4  5  6
2168

2169
julia> hvcat((3,3), a,b,c,d,e,f)
2170
2×3 Matrix{Int64}:
2171
 1  2  3
2172
 4  5  6
2173

2174
julia> [a b; c d; e f]
2175
3×2 Matrix{Int64}:
2176
 1  2
2177
 3  4
2178
 5  6
2179

2180
julia> hvcat((2,2,2), a,b,c,d,e,f)
2181
3×2 Matrix{Int64}:
2182
 1  2
2183
 3  4
2184
 5  6
2185
julia> hvcat((2,2,2), a,b,c,d,e,f) == hvcat(2, a,b,c,d,e,f)
2186
true
2187
```
2188
"""
UNCOV
2189
hvcat(rows::Tuple{Vararg{Int}}, xs::AbstractArray...) = typed_hvcat(promote_eltype(xs...), rows, xs...)
×
UNCOV
2190
hvcat(rows::Tuple{Vararg{Int}}, xs::AbstractArray{T}...) where {T} = typed_hvcat(T, rows, xs...)
×
2191

UNCOV
2192
rows_to_dimshape(rows::Tuple{Vararg{Int}}) = all(==(rows[1]), rows) ? (length(rows), rows[1]) : (rows, (sum(rows),))
×
UNCOV
2193
typed_hvcat(::Type{T}, rows::Tuple{Vararg{Int}}, as::AbstractVecOrMat...) where T = typed_hvncat(T, rows_to_dimshape(rows), true, as...)
×
2194

UNCOV
2195
hvcat(rows::Tuple{Vararg{Int}}) = []
×
2196
typed_hvcat(::Type{T}, rows::Tuple{Vararg{Int}}) where {T} = Vector{T}()
×
2197

UNCOV
2198
function hvcat(rows::Tuple{Vararg{Int}}, xs::T...) where T<:Number
×
UNCOV
2199
    nr = length(rows)
×
UNCOV
2200
    nc = rows[1]
×
2201

UNCOV
2202
    a = Matrix{T}(undef, nr, nc)
×
UNCOV
2203
    if length(a) != length(xs)
×
UNCOV
2204
        throw(ArgumentError("argument count does not match specified shape (expected $(length(a)), got $(length(xs)))"))
×
2205
    end
UNCOV
2206
    k = 1
×
UNCOV
2207
    @inbounds for i=1:nr
×
UNCOV
2208
        if nc != rows[i]
×
UNCOV
2209
            throw(DimensionMismatch("row $(i) has mismatched number of columns (expected $nc, got $(rows[i]))"))
×
2210
        end
UNCOV
2211
        for j=1:nc
×
UNCOV
2212
            a[i,j] = xs[k]
×
UNCOV
2213
            k += 1
×
UNCOV
2214
        end
×
UNCOV
2215
    end
×
UNCOV
2216
    a
×
2217
end
2218

UNCOV
2219
function hvcat_fill!(a::Array, xs::Tuple)
×
UNCOV
2220
    nr, nc = size(a,1), size(a,2)
×
UNCOV
2221
    len = length(xs)
×
UNCOV
2222
    if nr*nc != len
×
UNCOV
2223
        throw(ArgumentError("argument count $(len) does not match specified shape $((nr,nc))"))
×
2224
    end
UNCOV
2225
    k = 1
×
UNCOV
2226
    for i=1:nr
×
UNCOV
2227
        @inbounds for j=1:nc
×
UNCOV
2228
            a[i,j] = xs[k]
×
UNCOV
2229
            k += 1
×
UNCOV
2230
        end
×
UNCOV
2231
    end
×
UNCOV
2232
    a
×
2233
end
2234

UNCOV
2235
hvcat(rows::Tuple{Vararg{Int}}, xs::Number...) = typed_hvcat(promote_typeof(xs...), rows, xs...)
×
UNCOV
2236
hvcat(rows::Tuple{Vararg{Int}}, xs...) = typed_hvcat(promote_eltypeof(xs...), rows, xs...)
×
2237
# the following method is needed to provide a more specific one compared to LinearAlgebra/uniformscaling.jl
UNCOV
2238
hvcat(rows::Tuple{Vararg{Int}}, xs::Union{AbstractArray,Number}...) = typed_hvcat(promote_eltypeof(xs...), rows, xs...)
×
2239

UNCOV
2240
function typed_hvcat(::Type{T}, rows::Tuple{Vararg{Int}}, xs::Number...) where T
×
UNCOV
2241
    nr = length(rows)
×
UNCOV
2242
    nc = rows[1]
×
UNCOV
2243
    for i = 2:nr
×
UNCOV
2244
        if nc != rows[i]
×
UNCOV
2245
            throw(DimensionMismatch("row $(i) has mismatched number of columns (expected $nc, got $(rows[i]))"))
×
2246
        end
UNCOV
2247
    end
×
UNCOV
2248
    hvcat_fill!(Matrix{T}(undef, nr, nc), xs)
×
2249
end
2250

UNCOV
2251
typed_hvcat(::Type{T}, rows::Tuple{Vararg{Int}}, as...) where T = typed_hvncat(T, rows_to_dimshape(rows), true, as...)
×
2252

2253
## N-dimensional concatenation ##
2254

2255
"""
2256
    hvncat(dim::Int, row_first, values...)
2257
    hvncat(dims::Tuple{Vararg{Int}}, row_first, values...)
2258
    hvncat(shape::Tuple{Vararg{Tuple}}, row_first, values...)
2259

2260
Horizontal, vertical, and n-dimensional concatenation of many `values` in one call.
2261

2262
This function is called for block matrix syntax. The first argument either specifies the
2263
shape of the concatenation, similar to `hvcat`, as a tuple of tuples, or the dimensions that
2264
specify the key number of elements along each axis, and is used to determine the output
2265
dimensions. The `dims` form is more performant, and is used by default when the concatenation
2266
operation has the same number of elements along each axis (e.g., [a b; c d;;; e f ; g h]).
2267
The `shape` form is used when the number of elements along each axis is unbalanced
2268
(e.g., [a b ; c]). Unbalanced syntax needs additional validation overhead. The `dim` form
2269
is an optimization for concatenation along just one dimension. `row_first` indicates how
2270
`values` are ordered. The meaning of the first and second elements of `shape` are also
2271
swapped based on `row_first`.
2272

2273
# Examples
2274
```jldoctest
2275
julia> a, b, c, d, e, f = 1, 2, 3, 4, 5, 6
2276
(1, 2, 3, 4, 5, 6)
2277

2278
julia> [a b c;;; d e f]
2279
1×3×2 Array{Int64, 3}:
2280
[:, :, 1] =
2281
 1  2  3
2282

2283
[:, :, 2] =
2284
 4  5  6
2285

2286
julia> hvncat((2,1,3), false, a,b,c,d,e,f)
2287
2×1×3 Array{Int64, 3}:
2288
[:, :, 1] =
2289
 1
2290
 2
2291

2292
[:, :, 2] =
2293
 3
2294
 4
2295

2296
[:, :, 3] =
2297
 5
2298
 6
2299

2300
julia> [a b;;; c d;;; e f]
2301
1×2×3 Array{Int64, 3}:
2302
[:, :, 1] =
2303
 1  2
2304

2305
[:, :, 2] =
2306
 3  4
2307

2308
[:, :, 3] =
2309
 5  6
2310

2311
julia> hvncat(((3, 3), (3, 3), (6,)), true, a, b, c, d, e, f)
2312
1×3×2 Array{Int64, 3}:
2313
[:, :, 1] =
2314
 1  2  3
2315

2316
[:, :, 2] =
2317
 4  5  6
2318
```
2319

2320
# Examples for construction of the arguments
2321
```
2322
[a b c ; d e f ;;;
2323
 g h i ; j k l ;;;
2324
 m n o ; p q r ;;;
2325
 s t u ; v w x]
2326
⇒ dims = (2, 3, 4)
2327

2328
[a b ; c ;;; d ;;;;]
2329
 ___   _     _
2330
 2     1     1 = elements in each row (2, 1, 1)
2331
 _______     _
2332
 3           1 = elements in each column (3, 1)
2333
 _____________
2334
 4             = elements in each 3d slice (4,)
2335
 _____________
2336
 4             = elements in each 4d slice (4,)
2337
⇒ shape = ((2, 1, 1), (3, 1), (4,), (4,)) with `row_first` = true
2338
```
2339
"""
UNCOV
2340
hvncat(dimsshape::Tuple, row_first::Bool, xs...) = _hvncat(dimsshape, row_first, xs...)
×
UNCOV
2341
hvncat(dim::Int, xs...) = _hvncat(dim, true, xs...)
×
2342

UNCOV
2343
_hvncat(dimsshape::Union{Tuple, Int}, row_first::Bool) = _typed_hvncat(Any, dimsshape, row_first)
×
UNCOV
2344
_hvncat(dimsshape::Union{Tuple, Int}, row_first::Bool, xs...) = _typed_hvncat(promote_eltypeof(xs...), dimsshape, row_first, xs...)
×
UNCOV
2345
_hvncat(dimsshape::Union{Tuple, Int}, row_first::Bool, xs::T...) where T<:Number = _typed_hvncat(T, dimsshape, row_first, xs...)
×
2346
_hvncat(dimsshape::Union{Tuple, Int}, row_first::Bool, xs::Number...) = _typed_hvncat(promote_typeof(xs...), dimsshape, row_first, xs...)
×
2347
_hvncat(dimsshape::Union{Tuple, Int}, row_first::Bool, xs::AbstractArray...) = _typed_hvncat(promote_eltype(xs...), dimsshape, row_first, xs...)
×
UNCOV
2348
_hvncat(dimsshape::Union{Tuple, Int}, row_first::Bool, xs::AbstractArray{T}...) where T = _typed_hvncat(T, dimsshape, row_first, xs...)
×
2349

2350

UNCOV
2351
typed_hvncat(T::Type, dimsshape::Tuple, row_first::Bool, xs...) = _typed_hvncat(T, dimsshape, row_first, xs...)
×
UNCOV
2352
typed_hvncat(T::Type, dim::Int, xs...) = _typed_hvncat(T, Val(dim), xs...)
×
2353

2354
# 1-dimensional hvncat methods
2355

UNCOV
2356
_typed_hvncat(::Type, ::Val{0}) = _typed_hvncat_0d_only_one()
×
2357
_typed_hvncat(T::Type, ::Val{0}, x) = fill(convert(T, x))
×
UNCOV
2358
_typed_hvncat(T::Type, ::Val{0}, x::Number) = fill(convert(T, x))
×
UNCOV
2359
_typed_hvncat(T::Type, ::Val{0}, x::AbstractArray) = convert.(T, x)
×
2360
_typed_hvncat(::Type, ::Val{0}, ::Any...) = _typed_hvncat_0d_only_one()
×
UNCOV
2361
_typed_hvncat(::Type, ::Val{0}, ::Number...) = _typed_hvncat_0d_only_one()
×
2362
_typed_hvncat(::Type, ::Val{0}, ::AbstractArray...) = _typed_hvncat_0d_only_one()
×
2363

UNCOV
2364
_typed_hvncat_0d_only_one() =
×
2365
    throw(ArgumentError("a 0-dimensional array may only contain exactly one element"))
2366

2367
# `@constprop :aggressive` here to form constant `Val(dim)` type to get type stability
UNCOV
2368
@constprop :aggressive _typed_hvncat(T::Type, dim::Int, ::Bool, xs...) = _typed_hvncat(T, Val(dim), xs...) # catches from _hvncat type promoters
×
2369

UNCOV
2370
function _typed_hvncat(::Type{T}, ::Val{N}) where {T, N}
×
UNCOV
2371
    N < 0 &&
×
2372
        throw(ArgumentError("concatenation dimension must be non-negative"))
UNCOV
2373
    return Array{T, N}(undef, ntuple(x -> 0, Val(N)))
×
2374
end
2375

UNCOV
2376
function _typed_hvncat(T::Type, ::Val{N}, xs::Number...) where N
×
UNCOV
2377
    N < 0 &&
×
2378
        throw(ArgumentError("concatenation dimension must be non-negative"))
UNCOV
2379
    A = cat_similar(xs[1], T, (ntuple(x -> 1, Val(N - 1))..., length(xs)))
×
UNCOV
2380
    hvncat_fill!(A, false, xs)
×
UNCOV
2381
    return A
×
2382
end
2383

UNCOV
2384
function _typed_hvncat(::Type{T}, ::Val{N}, as::AbstractArray...) where {T, N}
×
2385
    # optimization for arrays that can be concatenated by copying them linearly into the destination
2386
    # conditions: the elements must all have 1-length dimensions above N
UNCOV
2387
    length(as) > 0 ||
×
2388
        throw(ArgumentError("must have at least one element"))
UNCOV
2389
    N < 0 &&
×
2390
        throw(ArgumentError("concatenation dimension must be non-negative"))
UNCOV
2391
    for a ∈ as
×
UNCOV
2392
        ndims(a) <= N || all(x -> size(a, x) == 1, (N + 1):ndims(a)) ||
×
UNCOV
2393
            return _typed_hvncat(T, (ntuple(x -> 1, Val(N - 1))..., length(as), 1), false, as...)
×
2394
            # the extra 1 is to avoid an infinite cycle
UNCOV
2395
    end
×
2396

UNCOV
2397
    nd = N
×
2398

UNCOV
2399
    Ndim = 0
×
UNCOV
2400
    for i ∈ eachindex(as)
×
UNCOV
2401
        Ndim += cat_size(as[i], N)
×
UNCOV
2402
        nd = max(nd, cat_ndims(as[i]))
×
UNCOV
2403
        for d ∈ 1:N - 1
×
UNCOV
2404
            cat_size(as[1], d) == cat_size(as[i], d) || throw(DimensionMismatch("mismatched size along axis $d in element $i"))
×
UNCOV
2405
        end
×
UNCOV
2406
    end
×
2407

UNCOV
2408
    A = cat_similar(as[1], T, (ntuple(d -> size(as[1], d), N - 1)..., Ndim, ntuple(x -> 1, nd - N)...))
×
UNCOV
2409
    k = 1
×
UNCOV
2410
    for a ∈ as
×
UNCOV
2411
        for i ∈ eachindex(a)
×
UNCOV
2412
            A[k] = a[i]
×
UNCOV
2413
            k += 1
×
UNCOV
2414
        end
×
UNCOV
2415
    end
×
UNCOV
2416
    return A
×
2417
end
2418

UNCOV
2419
function _typed_hvncat(::Type{T}, ::Val{N}, as...) where {T, N}
×
UNCOV
2420
    length(as) > 0 ||
×
2421
        throw(ArgumentError("must have at least one element"))
UNCOV
2422
    N < 0 &&
×
2423
        throw(ArgumentError("concatenation dimension must be non-negative"))
UNCOV
2424
    nd = N
×
UNCOV
2425
    Ndim = 0
×
UNCOV
2426
    for i ∈ eachindex(as)
×
UNCOV
2427
        Ndim += cat_size(as[i], N)
×
UNCOV
2428
        nd = max(nd, cat_ndims(as[i]))
×
UNCOV
2429
        for d ∈ 1:N-1
×
UNCOV
2430
            cat_size(as[i], d) == 1 ||
×
2431
                throw(DimensionMismatch("all dimensions of element $i other than $N must be of length 1"))
UNCOV
2432
        end
×
UNCOV
2433
    end
×
2434

UNCOV
2435
    A = Array{T, nd}(undef, ntuple(x -> 1, Val(N - 1))..., Ndim, ntuple(x -> 1, nd - N)...)
×
2436

UNCOV
2437
    k = 1
×
UNCOV
2438
    for a ∈ as
×
UNCOV
2439
        if a isa AbstractArray
×
UNCOV
2440
            lena = length(a)
×
UNCOV
2441
            copyto!(A, k, a, 1, lena)
×
UNCOV
2442
            k += lena
×
2443
        else
UNCOV
2444
            A[k] = a
×
UNCOV
2445
            k += 1
×
2446
        end
UNCOV
2447
    end
×
UNCOV
2448
    return A
×
2449
end
2450

2451
# 0-dimensional cases for balanced and unbalanced hvncat method
2452

UNCOV
2453
_typed_hvncat(T::Type, ::Tuple{}, ::Bool, x...) = _typed_hvncat(T, Val(0), x...)
×
UNCOV
2454
_typed_hvncat(T::Type, ::Tuple{}, ::Bool, x::Number...) = _typed_hvncat(T, Val(0), x...)
×
2455

2456

2457
# balanced dimensions hvncat methods
2458

UNCOV
2459
_typed_hvncat(T::Type, dims::Tuple{Int}, ::Bool, as...) = _typed_hvncat_1d(T, dims[1], Val(false), as...)
×
UNCOV
2460
_typed_hvncat(T::Type, dims::Tuple{Int}, ::Bool, as::Number...) = _typed_hvncat_1d(T, dims[1], Val(false), as...)
×
2461

UNCOV
2462
function _typed_hvncat_1d(::Type{T}, ds::Int, ::Val{row_first}, as...) where {T, row_first}
×
UNCOV
2463
    lengthas = length(as)
×
UNCOV
2464
    ds > 0 ||
×
2465
        throw(ArgumentError("`dimsshape` argument must consist of positive integers"))
UNCOV
2466
    lengthas == ds ||
×
2467
        throw(ArgumentError("number of elements does not match `dimshape` argument; expected $ds, got $lengthas"))
UNCOV
2468
    if row_first
×
UNCOV
2469
        return _typed_hvncat(T, Val(2), as...)
×
2470
    else
UNCOV
2471
        return _typed_hvncat(T, Val(1), as...)
×
2472
    end
2473
end
2474

UNCOV
2475
function _typed_hvncat(::Type{T}, dims::NTuple{N, Int}, row_first::Bool, xs::Number...) where {T, N}
×
UNCOV
2476
    all(>(0), dims) ||
×
2477
        throw(ArgumentError("`dims` argument must contain positive integers"))
UNCOV
2478
    A = Array{T, N}(undef, dims...)
×
UNCOV
2479
    lengtha = length(A)  # Necessary to store result because throw blocks are being deoptimized right now, which leads to excessive allocations
×
UNCOV
2480
    lengthx = length(xs) # Cuts from 3 allocations to 1.
×
UNCOV
2481
    if lengtha != lengthx
×
2482
       throw(ArgumentError("argument count does not match specified shape (expected $lengtha, got $lengthx)"))
×
2483
    end
UNCOV
2484
    hvncat_fill!(A, row_first, xs)
×
UNCOV
2485
    return A
×
2486
end
2487

UNCOV
2488
function hvncat_fill!(A::Array, row_first::Bool, xs::Tuple)
×
UNCOV
2489
    nr, nc = size(A, 1), size(A, 2)
×
UNCOV
2490
    na = prod(size(A)[3:end])
×
UNCOV
2491
    len = length(xs)
×
UNCOV
2492
    nrc = nr * nc
×
UNCOV
2493
    if nrc * na != len
×
2494
        throw(ArgumentError("argument count $(len) does not match specified shape $(size(A))"))
×
2495
    end
2496
    # putting these in separate functions leads to unnecessary allocations
UNCOV
2497
    if row_first
×
UNCOV
2498
        k = 1
×
UNCOV
2499
        for d ∈ 1:na
×
UNCOV
2500
            dd = nrc * (d - 1)
×
UNCOV
2501
            for i ∈ 1:nr
×
UNCOV
2502
                Ai = dd + i
×
UNCOV
2503
                for j ∈ 1:nc
×
UNCOV
2504
                    @inbounds A[Ai] = xs[k]
×
UNCOV
2505
                    k += 1
×
UNCOV
2506
                    Ai += nr
×
UNCOV
2507
                end
×
UNCOV
2508
            end
×
UNCOV
2509
        end
×
2510
    else
UNCOV
2511
        for k ∈ eachindex(xs)
×
UNCOV
2512
            @inbounds A[k] = xs[k]
×
UNCOV
2513
        end
×
2514
    end
2515
end
2516

UNCOV
2517
function _typed_hvncat(T::Type, dims::NTuple{N, Int}, row_first::Bool, as...) where {N}
×
2518
    # function barrier after calculating the max is necessary for high performance
UNCOV
2519
    nd = max(maximum(cat_ndims(a) for a ∈ as), N)
×
UNCOV
2520
    return _typed_hvncat_dims(T, (dims..., ntuple(x -> 1, nd - N)...), row_first, as)
×
2521
end
2522

UNCOV
2523
function _typed_hvncat_dims(::Type{T}, dims::NTuple{N, Int}, row_first::Bool, as::Tuple) where {T, N}
×
UNCOV
2524
    length(as) > 0 ||
×
2525
        throw(ArgumentError("must have at least one element"))
UNCOV
2526
    all(>(0), dims) ||
×
2527
        throw(ArgumentError("`dims` argument must contain positive integers"))
2528

UNCOV
2529
    d1 = row_first ? 2 : 1
×
UNCOV
2530
    d2 = row_first ? 1 : 2
×
2531

UNCOV
2532
    outdims = zeros(Int, N)
×
2533

2534
    # validate shapes for lowest level of concatenation
UNCOV
2535
    d = findfirst(>(1), dims)
×
UNCOV
2536
    if d !== nothing # all dims are 1
×
UNCOV
2537
        if row_first && d < 3
×
UNCOV
2538
            d = d == 1 ? 2 : 1
×
2539
        end
UNCOV
2540
        nblocks = length(as) ÷ dims[d]
×
UNCOV
2541
        for b ∈ 1:nblocks
×
UNCOV
2542
            offset = ((b - 1) * dims[d])
×
UNCOV
2543
            startelementi = offset + 1
×
UNCOV
2544
            for i ∈ offset .+ (2:dims[d])
×
UNCOV
2545
                for dd ∈ 1:N
×
UNCOV
2546
                    dd == d && continue
×
UNCOV
2547
                    if cat_size(as[startelementi], dd) != cat_size(as[i], dd)
×
UNCOV
2548
                        throw(DimensionMismatch("incompatible shape in element $i"))
×
2549
                    end
UNCOV
2550
                end
×
UNCOV
2551
            end
×
UNCOV
2552
        end
×
2553
    end
2554

2555
    # discover number of rows or columns
UNCOV
2556
    for i ∈ 1:dims[d1]
×
UNCOV
2557
        outdims[d1] += cat_size(as[i], d1)
×
UNCOV
2558
    end
×
2559

UNCOV
2560
    currentdims = zeros(Int, N)
×
UNCOV
2561
    blockcount = 0
×
UNCOV
2562
    elementcount = 0
×
UNCOV
2563
    for i ∈ eachindex(as)
×
UNCOV
2564
        elementcount += cat_length(as[i])
×
UNCOV
2565
        currentdims[d1] += cat_size(as[i], d1)
×
UNCOV
2566
        if currentdims[d1] == outdims[d1]
×
UNCOV
2567
            currentdims[d1] = 0
×
UNCOV
2568
            for d ∈ (d2, 3:N...)
×
UNCOV
2569
                currentdims[d] += cat_size(as[i], d)
×
UNCOV
2570
                if outdims[d] == 0 # unfixed dimension
×
UNCOV
2571
                    blockcount += 1
×
UNCOV
2572
                    if blockcount == dims[d]
×
UNCOV
2573
                        outdims[d] = currentdims[d]
×
UNCOV
2574
                        currentdims[d] = 0
×
UNCOV
2575
                        blockcount = 0
×
2576
                    else
UNCOV
2577
                        break
×
2578
                    end
2579
                else # fixed dimension
UNCOV
2580
                    if currentdims[d] == outdims[d] # end of dimension
×
UNCOV
2581
                        currentdims[d] = 0
×
UNCOV
2582
                    elseif currentdims[d] < outdims[d] # dimension in progress
×
UNCOV
2583
                        break
×
2584
                    else # exceeded dimension
2585
                        throw(DimensionMismatch("argument $i has too many elements along axis $d"))
×
2586
                    end
2587
                end
UNCOV
2588
            end
×
UNCOV
2589
        elseif currentdims[d1] > outdims[d1] # exceeded dimension
×
UNCOV
2590
            throw(DimensionMismatch("argument $i has too many elements along axis $d1"))
×
2591
        end
UNCOV
2592
    end
×
2593

UNCOV
2594
    outlen = prod(outdims)
×
UNCOV
2595
    elementcount == outlen ||
×
2596
        throw(DimensionMismatch("mismatched number of elements; expected $(outlen), got $(elementcount)"))
2597

2598
    # copy into final array
UNCOV
2599
    A = cat_similar(as[1], T, ntuple(i -> outdims[i], N))
×
2600
    # @assert all(==(0), currentdims)
UNCOV
2601
    outdims .= 0
×
UNCOV
2602
    hvncat_fill!(A, currentdims, outdims, d1, d2, as)
×
UNCOV
2603
    return A
×
2604
end
2605

2606

2607
# unbalanced dimensions hvncat methods
2608

UNCOV
2609
function _typed_hvncat(T::Type, shape::Tuple{Tuple}, row_first::Bool, xs...)
×
UNCOV
2610
    length(shape[1]) > 0 ||
×
2611
        throw(ArgumentError("each level of `shape` argument must have at least one value"))
UNCOV
2612
    return _typed_hvncat_1d(T, shape[1][1], Val(row_first), xs...)
×
2613
end
2614

UNCOV
2615
function _typed_hvncat(T::Type, shape::NTuple{N, Tuple}, row_first::Bool, as...) where {N}
×
2616
    # function barrier after calculating the max is necessary for high performance
UNCOV
2617
    nd = max(maximum(cat_ndims(a) for a ∈ as), N)
×
UNCOV
2618
    return _typed_hvncat_shape(T, (shape..., ntuple(x -> shape[end], nd - N)...), row_first, as)
×
2619
end
2620

UNCOV
2621
function _typed_hvncat_shape(::Type{T}, shape::NTuple{N, Tuple}, row_first, as::Tuple) where {T, N}
×
UNCOV
2622
    length(as) > 0 ||
×
2623
        throw(ArgumentError("must have at least one element"))
UNCOV
2624
    all(>(0), tuple((shape...)...)) ||
×
2625
        throw(ArgumentError("`shape` argument must consist of positive integers"))
2626

UNCOV
2627
    d1 = row_first ? 2 : 1
×
UNCOV
2628
    d2 = row_first ? 1 : 2
×
2629

UNCOV
2630
    shapev = collect(shape) # saves allocations later
×
UNCOV
2631
    all(!isempty, shapev) ||
×
2632
        throw(ArgumentError("each level of `shape` argument must have at least one value"))
UNCOV
2633
    length(shapev[end]) == 1 ||
×
2634
        throw(ArgumentError("last level of shape must contain only one integer"))
UNCOV
2635
    shapelength = shapev[end][1]
×
UNCOV
2636
    lengthas = length(as)
×
UNCOV
2637
    shapelength == lengthas || throw(ArgumentError("number of elements does not match shape; expected $(shapelength), got $lengthas)"))
×
2638
    # discover dimensions
UNCOV
2639
    nd = max(N, cat_ndims(as[1]))
×
UNCOV
2640
    outdims = fill(-1, nd)
×
UNCOV
2641
    currentdims = zeros(Int, nd)
×
UNCOV
2642
    blockcounts = zeros(Int, nd)
×
UNCOV
2643
    shapepos = ones(Int, nd)
×
2644

UNCOV
2645
    elementcount = 0
×
UNCOV
2646
    for i ∈ eachindex(as)
×
UNCOV
2647
        elementcount += cat_length(as[i])
×
UNCOV
2648
        wasstartblock = false
×
UNCOV
2649
        for d ∈ 1:N
×
UNCOV
2650
            ad = (d < 3 && row_first) ? (d == 1 ? 2 : 1) : d
×
UNCOV
2651
            dsize = cat_size(as[i], ad)
×
UNCOV
2652
            blockcounts[d] += 1
×
2653

UNCOV
2654
            if d == 1 || i == 1 || wasstartblock
×
UNCOV
2655
                currentdims[d] += dsize
×
UNCOV
2656
            elseif dsize != cat_size(as[i - 1], ad)
×
UNCOV
2657
                throw(DimensionMismatch("argument $i has a mismatched number of elements along axis $ad; \
×
2658
                                         expected $(cat_size(as[i - 1], ad)), got $dsize"))
2659
            end
2660

UNCOV
2661
            wasstartblock = blockcounts[d] == 1 # remember for next dimension
×
2662

UNCOV
2663
            isendblock = blockcounts[d] == shapev[d][shapepos[d]]
×
UNCOV
2664
            if isendblock
×
UNCOV
2665
                if outdims[d] == -1
×
UNCOV
2666
                    outdims[d] = currentdims[d]
×
UNCOV
2667
                elseif outdims[d] != currentdims[d]
×
UNCOV
2668
                    throw(DimensionMismatch("argument $i has a mismatched number of elements along axis $ad; \
×
2669
                                             expected $(abs(outdims[d] - (currentdims[d] - dsize))), got $dsize"))
2670
                end
UNCOV
2671
                currentdims[d] = 0
×
UNCOV
2672
                blockcounts[d] = 0
×
UNCOV
2673
                shapepos[d] += 1
×
UNCOV
2674
                d > 1 && (blockcounts[d - 1] == 0 ||
×
2675
                    throw(DimensionMismatch("shape in level $d is inconsistent; level counts must nest \
2676
                                             evenly into each other")))
2677
            end
UNCOV
2678
        end
×
UNCOV
2679
    end
×
2680

UNCOV
2681
    outlen = prod(outdims)
×
UNCOV
2682
    elementcount == outlen ||
×
2683
        throw(ArgumentError("mismatched number of elements; expected $(outlen), got $(elementcount)"))
2684

UNCOV
2685
    if row_first
×
UNCOV
2686
        outdims[1], outdims[2] = outdims[2], outdims[1]
×
2687
    end
2688

2689
    # @assert all(==(0), currentdims)
2690
    # @assert all(==(0), blockcounts)
2691

2692
    # copy into final array
UNCOV
2693
    A = cat_similar(as[1], T, ntuple(i -> outdims[i], nd))
×
UNCOV
2694
    hvncat_fill!(A, currentdims, blockcounts, d1, d2, as)
×
UNCOV
2695
    return A
×
2696
end
2697

UNCOV
2698
function hvncat_fill!(A::AbstractArray{T, N}, scratch1::Vector{Int}, scratch2::Vector{Int},
×
2699
                              d1::Int, d2::Int, as::Tuple) where {T, N}
UNCOV
2700
    N > 1 || throw(ArgumentError("dimensions of the destination array must be at least 2"))
×
UNCOV
2701
    length(scratch1) == length(scratch2) == N ||
×
2702
        throw(ArgumentError("scratch vectors must have as many elements as the destination array has dimensions"))
UNCOV
2703
    0 < d1 < 3 &&
×
2704
    0 < d2 < 3 &&
2705
    d1 != d2 ||
2706
        throw(ArgumentError("d1 and d2 must be either 1 or 2, exclusive."))
UNCOV
2707
    outdims = size(A)
×
UNCOV
2708
    offsets = scratch1
×
UNCOV
2709
    inneroffsets = scratch2
×
UNCOV
2710
    for a ∈ as
×
UNCOV
2711
        if isa(a, AbstractArray)
×
UNCOV
2712
            for ai ∈ a
×
UNCOV
2713
                @inbounds Ai = hvncat_calcindex(offsets, inneroffsets, outdims, N)
×
UNCOV
2714
                A[Ai] = ai
×
2715

UNCOV
2716
                @inbounds for j ∈ 1:N
×
UNCOV
2717
                    inneroffsets[j] += 1
×
UNCOV
2718
                    inneroffsets[j] < cat_size(a, j) && break
×
UNCOV
2719
                    inneroffsets[j] = 0
×
UNCOV
2720
                end
×
UNCOV
2721
            end
×
2722
        else
UNCOV
2723
            @inbounds Ai = hvncat_calcindex(offsets, inneroffsets, outdims, N)
×
UNCOV
2724
            A[Ai] = a
×
2725
        end
2726

UNCOV
2727
        @inbounds for j ∈ (d1, d2, 3:N...)
×
UNCOV
2728
            offsets[j] += cat_size(a, j)
×
UNCOV
2729
            offsets[j] < outdims[j] && break
×
UNCOV
2730
            offsets[j] = 0
×
UNCOV
2731
        end
×
UNCOV
2732
    end
×
2733
end
2734

UNCOV
2735
@propagate_inbounds function hvncat_calcindex(offsets::Vector{Int}, inneroffsets::Vector{Int},
×
2736
                                              outdims::Tuple{Vararg{Int}}, nd::Int)
UNCOV
2737
    Ai = inneroffsets[1] + offsets[1] + 1
×
UNCOV
2738
    for j ∈ 2:nd
×
UNCOV
2739
        increment = inneroffsets[j] + offsets[j]
×
UNCOV
2740
        for k ∈ 1:j-1
×
UNCOV
2741
            increment *= outdims[k]
×
UNCOV
2742
        end
×
UNCOV
2743
        Ai += increment
×
UNCOV
2744
    end
×
UNCOV
2745
    Ai
×
2746
end
2747

2748
"""
2749
    stack(iter; [dims])
2750

2751
Combine a collection of arrays (or other iterable objects) of equal size
2752
into one larger array, by arranging them along one or more new dimensions.
2753

2754
By default the axes of the elements are placed first,
2755
giving `size(result) = (size(first(iter))..., size(iter)...)`.
2756
This has the same order of elements as [`Iterators.flatten`](@ref)`(iter)`.
2757

2758
With keyword `dims::Integer`, instead the `i`th element of `iter` becomes the slice
2759
[`selectdim`](@ref)`(result, dims, i)`, so that `size(result, dims) == length(iter)`.
2760
In this case `stack` reverses the action of [`eachslice`](@ref) with the same `dims`.
2761

2762
The various [`cat`](@ref) functions also combine arrays. However, these all
2763
extend the arrays' existing (possibly trivial) dimensions, rather than placing
2764
the arrays along new dimensions.
2765
They also accept arrays as separate arguments, rather than a single collection.
2766

2767
!!! compat "Julia 1.9"
2768
    This function requires at least Julia 1.9.
2769

2770
# Examples
2771
```jldoctest
2772
julia> vecs = (1:2, [30, 40], Float32[500, 600]);
2773

2774
julia> mat = stack(vecs)
2775
2×3 Matrix{Float32}:
2776
 1.0  30.0  500.0
2777
 2.0  40.0  600.0
2778

2779
julia> mat == hcat(vecs...) == reduce(hcat, collect(vecs))
2780
true
2781

2782
julia> vec(mat) == vcat(vecs...) == reduce(vcat, collect(vecs))
2783
true
2784

2785
julia> stack(zip(1:4, 10:99))  # accepts any iterators of iterators
2786
2×4 Matrix{Int64}:
2787
  1   2   3   4
2788
 10  11  12  13
2789

2790
julia> vec(ans) == collect(Iterators.flatten(zip(1:4, 10:99)))
2791
true
2792

2793
julia> stack(vecs; dims=1)  # unlike any cat function, 1st axis of vecs[1] is 2nd axis of result
2794
3×2 Matrix{Float32}:
2795
   1.0    2.0
2796
  30.0   40.0
2797
 500.0  600.0
2798

2799
julia> x = rand(3,4);
2800

2801
julia> x == stack(eachcol(x)) == stack(eachrow(x), dims=1)  # inverse of eachslice
2802
true
2803
```
2804

2805
Higher-dimensional examples:
2806

2807
```jldoctest
2808
julia> A = rand(5, 7, 11);
2809

2810
julia> E = eachslice(A, dims=2);  # a vector of matrices
2811

2812
julia> (element = size(first(E)), container = size(E))
2813
(element = (5, 11), container = (7,))
2814

2815
julia> stack(E) |> size
2816
(5, 11, 7)
2817

2818
julia> stack(E) == stack(E; dims=3) == cat(E...; dims=3)
2819
true
2820

2821
julia> A == stack(E; dims=2)
2822
true
2823

2824
julia> M = (fill(10i+j, 2, 3) for i in 1:5, j in 1:7);
2825

2826
julia> (element = size(first(M)), container = size(M))
2827
(element = (2, 3), container = (5, 7))
2828

2829
julia> stack(M) |> size  # keeps all dimensions
2830
(2, 3, 5, 7)
2831

2832
julia> stack(M; dims=1) |> size  # vec(container) along dims=1
2833
(35, 2, 3)
2834

2835
julia> hvcat(5, M...) |> size  # hvcat puts matrices next to each other
2836
(14, 15)
2837
```
2838
"""
UNCOV
2839
stack(iter; dims=:) = _stack(dims, iter)
×
2840

2841
"""
2842
    stack(f, args...; [dims])
2843

2844
Apply a function to each element of a collection, and `stack` the result.
2845
Or to several collections, [`zip`](@ref)ped together.
2846

2847
The function should return arrays (or tuples, or other iterators) all of the same size.
2848
These become slices of the result, each separated along `dims` (if given) or by default
2849
along the last dimensions.
2850

2851
See also [`mapslices`](@ref), [`eachcol`](@ref).
2852

2853
# Examples
2854
```jldoctest
2855
julia> stack(c -> (c, c-32), "julia")
2856
2×5 Matrix{Char}:
2857
 'j'  'u'  'l'  'i'  'a'
2858
 'J'  'U'  'L'  'I'  'A'
2859

2860
julia> stack(eachrow([1 2 3; 4 5 6]), (10, 100); dims=1) do row, n
2861
         vcat(row, row .* n, row ./ n)
2862
       end
2863
2×9 Matrix{Float64}:
2864
 1.0  2.0  3.0   10.0   20.0   30.0  0.1   0.2   0.3
2865
 4.0  5.0  6.0  400.0  500.0  600.0  0.04  0.05  0.06
2866
```
2867
"""
UNCOV
2868
stack(f, iter; dims=:) = _stack(dims, f(x) for x in iter)
×
UNCOV
2869
stack(f, xs, yzs...; dims=:) = _stack(dims, f(xy...) for xy in zip(xs, yzs...))
×
2870

UNCOV
2871
_stack(dims::Union{Integer, Colon}, iter) = _stack(dims, IteratorSize(iter), iter)
×
2872

UNCOV
2873
_stack(dims, ::IteratorSize, iter) = _stack(dims, collect(iter))
×
2874

UNCOV
2875
function _stack(dims, ::Union{HasShape, HasLength}, iter)
×
UNCOV
2876
    S = @default_eltype iter
×
UNCOV
2877
    T = S != Union{} ? eltype(S) : Any  # Union{} occurs for e.g. stack(1,2), postpone the error
×
UNCOV
2878
    if isconcretetype(T)
×
UNCOV
2879
        _typed_stack(dims, T, S, iter)
×
2880
    else  # Need to look inside, but shouldn't run an expensive iterator twice:
UNCOV
2881
        array = iter isa Union{Tuple, AbstractArray} ? iter : collect(iter)
×
UNCOV
2882
        isempty(array) && return _empty_stack(dims, T, S, iter)
×
UNCOV
2883
        T2 = mapreduce(eltype, promote_type, array)
×
UNCOV
2884
        _typed_stack(dims, T2, eltype(array), array)
×
2885
    end
2886
end
2887

UNCOV
2888
function _typed_stack(::Colon, ::Type{T}, ::Type{S}, A, Aax=_iterator_axes(A)) where {T, S}
×
UNCOV
2889
    xit = iterate(A)
×
UNCOV
2890
    nothing === xit && return _empty_stack(:, T, S, A)
×
UNCOV
2891
    x1, _ = xit
×
UNCOV
2892
    ax1 = _iterator_axes(x1)
×
UNCOV
2893
    B = similar(_ensure_array(x1), T, ax1..., Aax...)
×
UNCOV
2894
    off = firstindex(B)
×
UNCOV
2895
    len = length(x1)
×
UNCOV
2896
    while xit !== nothing
×
UNCOV
2897
        x, state = xit
×
UNCOV
2898
        _stack_size_check(x, ax1)
×
UNCOV
2899
        copyto!(B, off, x)
×
UNCOV
2900
        off += len
×
UNCOV
2901
        xit = iterate(A, state)
×
UNCOV
2902
    end
×
UNCOV
2903
    B
×
2904
end
2905

UNCOV
2906
_iterator_axes(x) = _iterator_axes(x, IteratorSize(x))
×
UNCOV
2907
_iterator_axes(x, ::HasLength) = (OneTo(length(x)),)
×
UNCOV
2908
_iterator_axes(x, ::IteratorSize) = axes(x)
×
2909

2910
# For some dims values, stack(A; dims) == stack(vec(A)), and the : path will be faster
UNCOV
2911
_typed_stack(dims::Integer, ::Type{T}, ::Type{S}, A) where {T,S} =
×
2912
    _typed_stack(dims, T, S, IteratorSize(S), A)
UNCOV
2913
_typed_stack(dims::Integer, ::Type{T}, ::Type{S}, ::HasLength, A) where {T,S} =
×
2914
    _typed_stack(dims, T, S, HasShape{1}(), A)
UNCOV
2915
function _typed_stack(dims::Integer, ::Type{T}, ::Type{S}, ::HasShape{N}, A) where {T,S,N}
×
UNCOV
2916
    if dims == N+1
×
UNCOV
2917
        _typed_stack(:, T, S, A, (_vec_axis(A),))
×
2918
    else
UNCOV
2919
        _dim_stack(dims, T, S, A)
×
2920
    end
2921
end
UNCOV
2922
_typed_stack(dims::Integer, ::Type{T}, ::Type{S}, ::IteratorSize, A) where {T,S} =
×
2923
    _dim_stack(dims, T, S, A)
2924

UNCOV
2925
_vec_axis(A, ax=_iterator_axes(A)) = length(ax) == 1 ? only(ax) : OneTo(prod(length, ax; init=1))
×
2926

UNCOV
2927
@constprop :aggressive function _dim_stack(dims::Integer, ::Type{T}, ::Type{S}, A) where {T,S}
×
UNCOV
2928
    xit = Iterators.peel(A)
×
UNCOV
2929
    nothing === xit && return _empty_stack(dims, T, S, A)
×
UNCOV
2930
    x1, xrest = xit
×
UNCOV
2931
    ax1 = _iterator_axes(x1)
×
UNCOV
2932
    N1 = length(ax1)+1
×
UNCOV
2933
    dims in 1:N1 || throw(ArgumentError(LazyString("cannot stack slices ndims(x) = ", N1-1, " along dims = ", dims)))
×
2934

UNCOV
2935
    newaxis = _vec_axis(A)
×
UNCOV
2936
    outax = ntuple(d -> d==dims ? newaxis : ax1[d - (d>dims)], N1)
×
UNCOV
2937
    B = similar(_ensure_array(x1), T, outax...)
×
2938

UNCOV
2939
    if dims == 1
×
UNCOV
2940
        _dim_stack!(Val(1), B, x1, xrest)
×
UNCOV
2941
    elseif dims == 2
×
UNCOV
2942
        _dim_stack!(Val(2), B, x1, xrest)
×
2943
    else
UNCOV
2944
        _dim_stack!(Val(dims), B, x1, xrest)
×
2945
    end
UNCOV
2946
    B
×
2947
end
2948

UNCOV
2949
function _dim_stack!(::Val{dims}, B::AbstractArray, x1, xrest) where {dims}
×
UNCOV
2950
    before = ntuple(d -> Colon(), dims - 1)
×
UNCOV
2951
    after = ntuple(d -> Colon(), ndims(B) - dims)
×
2952

UNCOV
2953
    i = firstindex(B, dims)
×
UNCOV
2954
    copyto!(view(B, before..., i, after...), x1)
×
2955

UNCOV
2956
    for x in xrest
×
UNCOV
2957
        _stack_size_check(x, _iterator_axes(x1))
×
UNCOV
2958
        i += 1
×
UNCOV
2959
        @inbounds copyto!(view(B, before..., i, after...), x)
×
UNCOV
2960
    end
×
2961
end
2962

UNCOV
2963
@inline function _stack_size_check(x, ax1::Tuple)
×
UNCOV
2964
    if _iterator_axes(x) != ax1
×
UNCOV
2965
        uax1 = map(UnitRange, ax1)
×
UNCOV
2966
        uaxN = map(UnitRange, _iterator_axes(x))
×
UNCOV
2967
        throw(DimensionMismatch(
×
2968
            LazyString("stack expects uniform slices, got axes(x) == ", uaxN, " while first had ", uax1)))
2969
    end
2970
end
2971

UNCOV
2972
_ensure_array(x::AbstractArray) = x
×
UNCOV
2973
_ensure_array(x) = 1:0  # passed to similar, makes stack's output an Array
×
2974

UNCOV
2975
_empty_stack(_...) = throw(ArgumentError("`stack` on an empty collection is not allowed"))
×
2976

2977

2978
## Reductions and accumulates ##
2979

2980
function isequal(A::AbstractArray, B::AbstractArray)
2981
    if A === B return true end
1,365✔
2982
    if axes(A) != axes(B)
2,616✔
UNCOV
2983
        return false
×
2984
    end
2985
    for (a, b) in zip(A, B)
2,598✔
2986
        if !isequal(a, b)
153,661✔
2987
            return false
229✔
2988
        end
2989
    end
305,803✔
2990
    return true
1,079✔
2991
end
2992

UNCOV
2993
function cmp(A::AbstractVector, B::AbstractVector)
×
UNCOV
2994
    for (a, b) in zip(A, B)
×
UNCOV
2995
        if !isequal(a, b)
×
UNCOV
2996
            return isless(a, b) ? -1 : 1
×
2997
        end
UNCOV
2998
    end
×
UNCOV
2999
    return cmp(length(A), length(B))
×
3000
end
3001

3002
"""
3003
    isless(A::AbstractArray{<:Any,0}, B::AbstractArray{<:Any,0})
3004

3005
Return `true` when the only element of `A` is less than the only element of `B`.
3006
"""
UNCOV
3007
function isless(A::AbstractArray{<:Any,0}, B::AbstractArray{<:Any,0})
×
UNCOV
3008
    isless(only(A), only(B))
×
3009
end
3010

3011
"""
3012
    isless(A::AbstractVector, B::AbstractVector)
3013

3014
Return `true` when `A` is less than `B` in lexicographic order.
3015
"""
UNCOV
3016
isless(A::AbstractVector, B::AbstractVector) = cmp(A, B) < 0
×
3017

3018
function (==)(A::AbstractArray, B::AbstractArray)
9✔
3019
    if axes(A) != axes(B)
22✔
UNCOV
3020
        return false
×
3021
    end
3022
    anymissing = false
9✔
3023
    for (a, b) in zip(A, B)
19✔
3024
        eq = (a == b)
31✔
3025
        if ismissing(eq)
26✔
UNCOV
3026
            anymissing = true
×
3027
        elseif !eq
31✔
UNCOV
3028
            return false
×
3029
        end
3030
    end
54✔
3031
    return anymissing ? missing : true
11✔
3032
end
3033

3034
# _sub2ind and _ind2sub
3035
# fallbacks
3036
function _sub2ind(A::AbstractArray, I...)
UNCOV
3037
    @inline
×
3038
    _sub2ind(axes(A), I...)
621,817✔
3039
end
3040

UNCOV
3041
function _ind2sub(A::AbstractArray, ind)
×
UNCOV
3042
    @inline
×
UNCOV
3043
    _ind2sub(axes(A), ind)
×
3044
end
3045

3046
# 0-dimensional arrays and indexing with []
3047
_sub2ind(::Tuple{}) = 1
×
3048
_sub2ind(::DimsInteger) = 1
×
3049
_sub2ind(::Indices) = 1
×
UNCOV
3050
_sub2ind(::Tuple{}, I::Integer...) = (@inline; _sub2ind_recurse((), 1, 1, I...))
×
3051

3052
# Generic cases
UNCOV
3053
_sub2ind(dims::DimsInteger, I::Integer...) = (@inline; _sub2ind_recurse(dims, 1, 1, I...))
×
3054
_sub2ind(inds::Indices, I::Integer...) = (@inline; _sub2ind_recurse(inds, 1, 1, I...))
621,817✔
3055
# In 1d, there's a question of whether we're doing cartesian indexing
3056
# or linear indexing. Support only the former.
UNCOV
3057
_sub2ind(inds::Indices{1}, I::Integer...) =
×
3058
    throw(ArgumentError("Linear indexing is not defined for one-dimensional arrays"))
3059
_sub2ind(inds::Tuple{OneTo}, I::Integer...) = (@inline; _sub2ind_recurse(inds, 1, 1, I...)) # only OneTo is safe
×
3060
_sub2ind(inds::Tuple{OneTo}, i::Integer)    = i
×
3061

UNCOV
3062
_sub2ind_recurse(::Any, L, ind) = ind
×
UNCOV
3063
function _sub2ind_recurse(::Tuple{}, L, ind, i::Integer, I::Integer...)
×
UNCOV
3064
    @inline
×
UNCOV
3065
    _sub2ind_recurse((), L, ind+(i-1)*L, I...)
×
3066
end
3067
function _sub2ind_recurse(inds, L, ind, i::Integer, I::Integer...)
UNCOV
3068
    @inline
×
UNCOV
3069
    r1 = inds[1]
×
3070
    _sub2ind_recurse(tail(inds), nextL(L, r1), ind+offsetin(i, r1)*L, I...)
1,243,634✔
3071
end
3072

UNCOV
3073
nextL(L, l::Integer) = L*l
×
3074
nextL(L, r::AbstractUnitRange) = L*length(r)
621,817✔
3075
nextL(L, r::Slice) = L*length(r.indices)
×
UNCOV
3076
offsetin(i, l::Integer) = i-1
×
3077
offsetin(i, r::AbstractUnitRange) = i-first(r)
1,243,634✔
3078

3079
_ind2sub(::Tuple{}, ind::Integer) = (@inline; ind == 1 ? () : throw(BoundsError()))
×
UNCOV
3080
_ind2sub(dims::DimsInteger, ind::Integer) = (@inline; _ind2sub_recurse(dims, ind-1))
×
UNCOV
3081
_ind2sub(inds::Indices, ind::Integer)     = (@inline; _ind2sub_recurse(inds, ind-1))
×
UNCOV
3082
_ind2sub(inds::Indices{1}, ind::Integer) =
×
3083
    throw(ArgumentError("Linear indexing is not defined for one-dimensional arrays"))
UNCOV
3084
_ind2sub(inds::Tuple{OneTo}, ind::Integer) = (ind,)
×
3085

3086
_ind2sub_recurse(::Tuple{}, ind) = (ind+1,)
×
UNCOV
3087
function _ind2sub_recurse(indslast::NTuple{1}, ind)
×
UNCOV
3088
    @inline
×
UNCOV
3089
    (_lookup(ind, indslast[1]),)
×
3090
end
UNCOV
3091
function _ind2sub_recurse(inds, ind)
×
UNCOV
3092
    @inline
×
UNCOV
3093
    r1 = inds[1]
×
UNCOV
3094
    indnext, f, l = _div(ind, r1)
×
UNCOV
3095
    (ind-l*indnext+f, _ind2sub_recurse(tail(inds), indnext)...)
×
3096
end
3097

UNCOV
3098
_lookup(ind, d::Integer) = ind+1
×
UNCOV
3099
_lookup(ind, r::AbstractUnitRange) = ind+first(r)
×
UNCOV
3100
_div(ind, d::Integer) = div(ind, d), 1, d
×
UNCOV
3101
_div(ind, r::AbstractUnitRange) = (d = length(r); (div(ind, d), first(r), d))
×
3102

3103
# Vectorized forms
3104
function _sub2ind(inds::Indices{1}, I1::AbstractVector{T}, I::AbstractVector{T}...) where T<:Integer
×
3105
    throw(ArgumentError("Linear indexing is not defined for one-dimensional arrays"))
×
3106
end
3107
_sub2ind(inds::Tuple{OneTo}, I1::AbstractVector{T}, I::AbstractVector{T}...) where {T<:Integer} =
×
3108
    _sub2ind_vecs(inds, I1, I...)
3109
_sub2ind(inds::Union{DimsInteger,Indices}, I1::AbstractVector{T}, I::AbstractVector{T}...) where {T<:Integer} =
×
3110
    _sub2ind_vecs(inds, I1, I...)
3111
function _sub2ind_vecs(inds, I::AbstractVector...)
×
3112
    I1 = I[1]
×
3113
    Iinds = axes1(I1)
×
3114
    for j = 2:length(I)
×
3115
        axes1(I[j]) == Iinds || throw(DimensionMismatch("indices of I[1] ($(Iinds)) does not match indices of I[$j] ($(axes1(I[j])))"))
×
3116
    end
×
3117
    Iout = similar(I1)
×
3118
    _sub2ind!(Iout, inds, Iinds, I)
×
3119
    Iout
×
3120
end
3121

3122
function _sub2ind!(Iout, inds, Iinds, I)
×
3123
    @noinline
×
3124
    for i in Iinds
×
3125
        # Iout[i] = _sub2ind(inds, map(Ij -> Ij[i], I)...)
3126
        Iout[i] = sub2ind_vec(inds, i, I)
×
3127
    end
×
3128
    Iout
×
3129
end
3130

3131
sub2ind_vec(inds, i, I) = (@inline; _sub2ind(inds, _sub2ind_vec(i, I...)...))
×
3132
_sub2ind_vec(i, I1, I...) = (@inline; (I1[i], _sub2ind_vec(i, I...)...))
×
3133
_sub2ind_vec(i) = ()
×
3134

3135
function _ind2sub(inds::Union{DimsInteger{N},Indices{N}}, ind::AbstractVector{<:Integer}) where N
×
3136
    M = length(ind)
×
3137
    t = ntuple(n->similar(ind),Val(N))
×
3138
    for (i,idx) in pairs(IndexLinear(), ind)
×
3139
        sub = _ind2sub(inds, idx)
×
3140
        for j = 1:N
×
3141
            t[j][i] = sub[j]
×
3142
        end
×
3143
    end
×
3144
    t
×
3145
end
3146

3147
## iteration utilities ##
3148

3149
"""
3150
    foreach(f, c...) -> Nothing
3151

3152
Call function `f` on each element of iterable `c`.
3153
For multiple iterable arguments, `f` is called elementwise, and iteration stops when
3154
any iterator is finished.
3155

3156
`foreach` should be used instead of [`map`](@ref) when the results of `f` are not
3157
needed, for example in `foreach(println, array)`.
3158

3159
# Examples
3160
```jldoctest
3161
julia> tri = 1:3:7; res = Int[];
3162

3163
julia> foreach(x -> push!(res, x^2), tri)
3164

3165
julia> res
3166
3-element Vector{$(Int)}:
3167
  1
3168
 16
3169
 49
3170

3171
julia> foreach((x, y) -> println(x, " with ", y), tri, 'a':'z')
3172
1 with a
3173
4 with b
3174
7 with c
3175
```
3176
"""
3177
foreach(f, itr) = (for x in itr; f(x); end; nothing)
53✔
UNCOV
3178
foreach(f, itr, itrs...) = (for z in zip(itr, itrs...); f(z...); end; nothing)
×
3179

3180
## map over arrays ##
3181

3182
## transform any set of dimensions
3183
## dims specifies which dimensions will be transformed. for example
3184
## dims==1:2 will call f on all slices A[:,:,...]
3185
"""
3186
    mapslices(f, A; dims)
3187

3188
Transform the given dimensions of array `A` by applying a function `f` on each slice
3189
of the form `A[..., :, ..., :, ...]`, with a colon at each `d` in `dims`. The results are
3190
concatenated along the remaining dimensions.
3191

3192
For example, if `dims = [1,2]` and `A` is 4-dimensional, then `f` is called on `x = A[:,:,i,j]`
3193
for all `i` and `j`, and `f(x)` becomes `R[:,:,i,j]` in the result `R`.
3194

3195
See also [`eachcol`](@ref) or [`eachslice`](@ref), used with [`map`](@ref) or [`stack`](@ref).
3196

3197
# Examples
3198
```jldoctest
3199
julia> A = reshape(1:30,(2,5,3))
3200
2×5×3 reshape(::UnitRange{$Int}, 2, 5, 3) with eltype $Int:
3201
[:, :, 1] =
3202
 1  3  5  7   9
3203
 2  4  6  8  10
3204

3205
[:, :, 2] =
3206
 11  13  15  17  19
3207
 12  14  16  18  20
3208

3209
[:, :, 3] =
3210
 21  23  25  27  29
3211
 22  24  26  28  30
3212

3213
julia> f(x::Matrix) = fill(x[1,1], 1,4);  # returns a 1×4 matrix
3214

3215
julia> B = mapslices(f, A, dims=(1,2))
3216
1×4×3 Array{$Int, 3}:
3217
[:, :, 1] =
3218
 1  1  1  1
3219

3220
[:, :, 2] =
3221
 11  11  11  11
3222

3223
[:, :, 3] =
3224
 21  21  21  21
3225

3226
julia> f2(x::AbstractMatrix) = fill(x[1,1], 1,4);
3227

3228
julia> B == stack(f2, eachslice(A, dims=3))
3229
true
3230

3231
julia> g(x) = x[begin] // x[end-1];  # returns a number
3232

3233
julia> mapslices(g, A, dims=[1,3])
3234
1×5×1 Array{Rational{$Int}, 3}:
3235
[:, :, 1] =
3236
 1//21  3//23  1//5  7//27  9//29
3237

3238
julia> map(g, eachslice(A, dims=2))
3239
5-element Vector{Rational{$Int}}:
3240
 1//21
3241
 3//23
3242
 1//5
3243
 7//27
3244
 9//29
3245

3246
julia> mapslices(sum, A; dims=(1,3)) == sum(A; dims=(1,3))
3247
true
3248
```
3249

3250
Notice that in `eachslice(A; dims=2)`, the specified dimension is the
3251
one *without* a colon in the slice. This is `view(A,:,i,:)`, whereas
3252
`mapslices(f, A; dims=(1,3))` uses `A[:,i,:]`. The function `f` may mutate
3253
values in the slice without affecting `A`.
3254
"""
UNCOV
3255
@constprop :aggressive function mapslices(f, A::AbstractArray; dims)
×
UNCOV
3256
    isempty(dims) && return map(f, A)
×
3257

UNCOV
3258
    for d in dims
×
UNCOV
3259
        d isa Integer || throw(ArgumentError("mapslices: dimension must be an integer, got $d"))
×
UNCOV
3260
        d >= 1 || throw(ArgumentError("mapslices: dimension must be ≥ 1, got $d"))
×
3261
        # Indexing a matrix M[:,1,:] produces a 1-column matrix, but dims=(1,3) here
3262
        # would otherwise ignore 3, and slice M[:,i]. Previously this gave error:
3263
        # BoundsError: attempt to access 2-element Vector{Any} at index [3]
UNCOV
3264
        d > ndims(A) && throw(ArgumentError("mapslices does not accept dimensions > ndims(A) = $(ndims(A)), got $d"))
×
UNCOV
3265
    end
×
UNCOV
3266
    dim_mask = ntuple(d -> d in dims, ndims(A))
×
3267

3268
    # Apply the function to the first slice in order to determine the next steps
UNCOV
3269
    idx1 = ntuple(d -> d in dims ? (:) : firstindex(A,d), ndims(A))
×
UNCOV
3270
    Aslice = A[idx1...]
×
UNCOV
3271
    r1 = f(Aslice)
×
3272

UNCOV
3273
    res1 = if r1 isa AbstractArray && ndims(r1) > 0
×
UNCOV
3274
        n = sum(dim_mask)
×
UNCOV
3275
        if ndims(r1) > n && any(ntuple(d -> size(r1,d+n)>1, ndims(r1)-n))
×
UNCOV
3276
            s = size(r1)[1:n]
×
UNCOV
3277
            throw(DimensionMismatch("mapslices cannot assign slice f(x) of size $(size(r1)) into output of size $s"))
×
3278
        end
UNCOV
3279
        r1
×
3280
    else
3281
        # If the result of f on a single slice is a scalar then we add singleton
3282
        # dimensions. When adding the dimensions, we have to respect the
3283
        # index type of the input array (e.g. in the case of OffsetArrays)
UNCOV
3284
        _res1 = similar(Aslice, typeof(r1), reduced_indices(Aslice, 1:ndims(Aslice)))
×
UNCOV
3285
        _res1[begin] = r1
×
UNCOV
3286
        _res1
×
3287
    end
3288

3289
    # Determine result size and allocate. We always pad ndims(res1) out to length(dims):
UNCOV
3290
    din = Ref(0)
×
UNCOV
3291
    Rsize = ntuple(ndims(A)) do d
×
UNCOV
3292
        if d in dims
×
UNCOV
3293
            axes(res1, din[] += 1)
×
3294
        else
UNCOV
3295
            axes(A,d)
×
3296
        end
3297
    end
UNCOV
3298
    R = similar(res1, Rsize)
×
3299

3300
    # Determine iteration space. It will be convenient in the loop to mask N-dimensional
3301
    # CartesianIndices, with some trivial dimensions:
UNCOV
3302
    itershape = ntuple(d -> d in dims ? Base.OneTo(1) : axes(A,d), ndims(A))
×
UNCOV
3303
    indices = Iterators.drop(CartesianIndices(itershape), 1)
×
3304

3305
    # That skips the first element, which we already have:
UNCOV
3306
    ridx = ntuple(d -> d in dims ? Slice(axes(R,d)) : firstindex(A,d), ndims(A))
×
UNCOV
3307
    concatenate_setindex!(R, res1, ridx...)
×
3308

3309
    # In some cases, we can re-use the first slice for a dramatic performance
3310
    # increase. The slice itself must be mutable and the result cannot contain
3311
    # any mutable containers. The following errs on the side of being overly
3312
    # strict (#18570 & #21123).
UNCOV
3313
    safe_for_reuse = isa(Aslice, StridedArray) &&
×
3314
                     (isa(r1, Number) || (isa(r1, AbstractArray) && eltype(r1) <: Number))
3315

UNCOV
3316
    _inner_mapslices!(R, indices, f, A, dim_mask, Aslice, safe_for_reuse)
×
UNCOV
3317
    return R
×
3318
end
3319

UNCOV
3320
@noinline function _inner_mapslices!(R, indices, f, A, dim_mask, Aslice, safe_for_reuse)
×
UNCOV
3321
    must_extend = any(dim_mask .& size(R) .> 1)
×
UNCOV
3322
    if safe_for_reuse
×
3323
        # when f returns an array, R[ridx...] = f(Aslice) line copies elements,
3324
        # so we can reuse Aslice
UNCOV
3325
        for I in indices
×
UNCOV
3326
            idx = ifelse.(dim_mask, Slice.(axes(A)), Tuple(I))
×
UNCOV
3327
            _unsafe_getindex!(Aslice, A, idx...)
×
UNCOV
3328
            r = f(Aslice)
×
UNCOV
3329
            if r isa AbstractArray || must_extend
×
UNCOV
3330
                ridx = ifelse.(dim_mask, Slice.(axes(R)), Tuple(I))
×
UNCOV
3331
                R[ridx...] = r
×
3332
            else
UNCOV
3333
                ridx = ifelse.(dim_mask, first.(axes(R)), Tuple(I))
×
UNCOV
3334
                R[ridx...] = r
×
3335
            end
UNCOV
3336
        end
×
3337
    else
3338
        # we can't guarantee safety (#18524), so allocate new storage for each slice
UNCOV
3339
        for I in indices
×
UNCOV
3340
            idx = ifelse.(dim_mask, Slice.(axes(A)), Tuple(I))
×
UNCOV
3341
            ridx = ifelse.(dim_mask, Slice.(axes(R)), Tuple(I))
×
UNCOV
3342
            concatenate_setindex!(R, f(A[idx...]), ridx...)
×
UNCOV
3343
        end
×
3344
    end
3345
end
3346

UNCOV
3347
concatenate_setindex!(R, v, I...) = (R[I...] .= (v,); R)
×
UNCOV
3348
concatenate_setindex!(R, X::AbstractArray, I...) = (R[I...] = X)
×
3349

3350
## 1 argument
3351

3352
function map!(f::F, dest::AbstractArray, A::AbstractArray) where F
4✔
3353
    for (i,j) in zip(eachindex(dest),eachindex(A))
6✔
3354
        val = f(@inbounds A[j])
2✔
3355
        @inbounds dest[i] = val
2✔
3356
    end
2✔
3357
    return dest
4✔
3358
end
3359

3360
# map on collections
3361
map(f, A::AbstractArray) = collect_similar(A, Generator(f,A))
182✔
3362

3363
mapany(f, A::AbstractArray) = map!(f, Vector{Any}(undef, length(A)), A)
4✔
3364
mapany(f, itr) = Any[f(x) for x in itr]
×
3365

3366
"""
3367
    map(f, c...) -> collection
3368

3369
Transform collection `c` by applying `f` to each element. For multiple collection arguments,
3370
apply `f` elementwise, and stop when any of them is exhausted.
3371

3372
The element type of the result is determined in the same manner as in [`collect`](@ref).
3373

3374
See also [`map!`](@ref), [`foreach`](@ref), [`mapreduce`](@ref), [`mapslices`](@ref), [`zip`](@ref), [`Iterators.map`](@ref).
3375

3376
# Examples
3377
```jldoctest
3378
julia> map(x -> x * 2, [1, 2, 3])
3379
3-element Vector{Int64}:
3380
 2
3381
 4
3382
 6
3383

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

UNCOV
3393
map(f, ::AbstractDict) = error("map is not defined on dictionaries")
×
UNCOV
3394
map(f, ::AbstractSet) = error("map is not defined on sets")
×
3395

3396
## 2 argument
UNCOV
3397
function map!(f::F, dest::AbstractArray, A::AbstractArray, B::AbstractArray) where F
×
UNCOV
3398
    for (i, j, k) in zip(eachindex(dest), eachindex(A), eachindex(B))
×
UNCOV
3399
        @inbounds a, b = A[j], B[k]
×
UNCOV
3400
        val = f(a, b)
×
UNCOV
3401
        @inbounds dest[i] = val
×
UNCOV
3402
    end
×
UNCOV
3403
    return dest
×
3404
end
3405

3406
## N argument
3407

UNCOV
3408
@inline ith_all(i, ::Tuple{}) = ()
×
UNCOV
3409
function ith_all(i, as)
×
UNCOV
3410
    @_propagate_inbounds_meta
×
UNCOV
3411
    return (as[1][i], ith_all(i, tail(as))...)
×
3412
end
3413

UNCOV
3414
function map_n!(f::F, dest::AbstractArray, As) where F
×
UNCOV
3415
    idxs1 = LinearIndices(As[1])
×
UNCOV
3416
    @boundscheck LinearIndices(dest) == idxs1 && all(x -> LinearIndices(x) == idxs1, As)
×
UNCOV
3417
    for i = idxs1
×
UNCOV
3418
        @inbounds I = ith_all(i, As)
×
UNCOV
3419
        val = f(I...)
×
UNCOV
3420
        @inbounds dest[i] = val
×
UNCOV
3421
    end
×
UNCOV
3422
    return dest
×
3423
end
3424

3425
"""
3426
    map!(function, destination, collection...)
3427

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

3431
$(_DOCS_ALIASING_WARNING)
3432

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

3435
# Examples
3436
```jldoctest
3437
julia> a = zeros(3);
3438

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

3441
julia> a
3442
3-element Vector{Float64}:
3443
 2.0
3444
 4.0
3445
 6.0
3446

3447
julia> map!(+, zeros(Int, 5), 100:999, 1:3)
3448
5-element Vector{$(Int)}:
3449
 101
3450
 103
3451
 105
3452
   0
3453
   0
3454
```
3455
"""
UNCOV
3456
function map!(f::F, dest::AbstractArray, As::AbstractArray...) where {F}
×
UNCOV
3457
    @assert !isempty(As) # should dispatch to map!(f, A)
×
UNCOV
3458
    map_n!(f, dest, As)
×
3459
end
3460

3461
"""
3462
    map!(function, array)
3463

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

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

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

3475
julia> a
3476
2×3 Matrix{$Int}:
3477
  1    8   27
3478
 64  125  216
3479
```
3480
"""
UNCOV
3481
map!(f::F, inout::AbstractArray) where F = map!(f, inout, inout)
×
3482

3483
"""
3484
    map(f, A::AbstractArray...) -> N-array
3485

3486
When acting on multi-dimensional arrays of the same [`ndims`](@ref),
3487
they must all have the same [`axes`](@ref), and the answer will too.
3488

3489
See also [`broadcast`](@ref), which allows mismatched sizes.
3490

3491
# Examples
3492
```
3493
julia> map(//, [1 2; 3 4], [4 3; 2 1])
3494
2×2 Matrix{Rational{$Int}}:
3495
 1//4  2//3
3496
 3//2  4//1
3497

3498
julia> map(+, [1 2; 3 4], zeros(2,1))
3499
ERROR: DimensionMismatch
3500

3501
julia> map(+, [1 2; 3 4], [1,10,100,1000], zeros(3,1))  # iterates until 3rd is exhausted
3502
3-element Vector{Float64}:
3503
   2.0
3504
  13.0
3505
 102.0
3506
```
3507
"""
UNCOV
3508
map(f, it, iters...) = collect(Generator(f, it, iters...))
×
3509

3510
# Generic versions of push! for AbstractVector
3511
# These are specialized further for Vector for faster resizing and setindexing
UNCOV
3512
function push!(a::AbstractVector{T}, item) where T
×
3513
    # convert first so we don't grow the array if the assignment won't work
UNCOV
3514
    itemT = item isa T ? item : convert(T, item)::T
×
UNCOV
3515
    new_length = length(a) + 1
×
UNCOV
3516
    resize!(a, new_length)
×
UNCOV
3517
    a[end] = itemT
×
UNCOV
3518
    return a
×
3519
end
3520

3521
# specialize and optimize the single argument case
UNCOV
3522
function push!(a::AbstractVector{Any}, @nospecialize x)
×
UNCOV
3523
    new_length = length(a) + 1
×
UNCOV
3524
    resize!(a, new_length)
×
UNCOV
3525
    a[end] = x
×
UNCOV
3526
    return a
×
3527
end
UNCOV
3528
function push!(a::AbstractVector{Any}, @nospecialize x...)
×
UNCOV
3529
    @_terminates_locally_meta
×
UNCOV
3530
    na = length(a)
×
UNCOV
3531
    nx = length(x)
×
UNCOV
3532
    resize!(a, na + nx)
×
UNCOV
3533
    e = lastindex(a) - nx
×
UNCOV
3534
    for i = 1:nx
×
UNCOV
3535
        a[e+i] = x[i]
×
UNCOV
3536
    end
×
UNCOV
3537
    return a
×
3538
end
3539

3540
# multi-item push!, pushfirst! (built on top of type-specific 1-item version)
3541
# (note: must not cause a dispatch loop when 1-item case is not defined)
UNCOV
3542
push!(A, a, b) = push!(push!(A, a), b)
×
UNCOV
3543
push!(A, a, b, c...) = push!(push!(A, a, b), c...)
×
3544
pushfirst!(A, a, b) = pushfirst!(pushfirst!(A, b), a)
×
UNCOV
3545
pushfirst!(A, a, b, c...) = pushfirst!(pushfirst!(A, c...), a, b)
×
3546

3547
# sizehint! does not nothing by default
UNCOV
3548
sizehint!(a::AbstractVector, _) = a
×
3549

3550
## hashing AbstractArray ##
3551

3552
const hash_abstractarray_seed = UInt === UInt64 ? 0x7e2d6fb6448beb77 : 0xd4514ce5
3553
function hash(A::AbstractArray, h::UInt)
1,831✔
3554
    h += hash_abstractarray_seed
1,831✔
3555
    # Axes are themselves AbstractArrays, so hashing them directly would stack overflow
3556
    # Instead hash the tuple of firsts and lasts along each dimension
3557
    h = hash(map(first, axes(A)), h)
1,831✔
3558
    h = hash(map(last, axes(A)), h)
1,831✔
3559

3560
    # For short arrays, it's not worth doing anything complicated
3561
    if length(A) < 8192
1,831✔
3562
        for x in A
3,644✔
3563
            h = hash(x, h)
260,393✔
3564
        end
518,973✔
3565
        return h
1,831✔
3566
    end
3567

3568
    # Goal: Hash approximately log(N) entries with a higher density of hashed elements
3569
    # weighted towards the end and special consideration for repeated values. Colliding
3570
    # hashes will often subsequently be compared by equality -- and equality between arrays
3571
    # works elementwise forwards and is short-circuiting. This means that a collision
3572
    # between arrays that differ by elements at the beginning is cheaper than one where the
3573
    # difference is towards the end. Furthermore, choosing `log(N)` arbitrary entries from a
3574
    # sparse array will likely only choose the same element repeatedly (zero in this case).
3575

3576
    # To achieve this, we work backwards, starting by hashing the last element of the
3577
    # array. After hashing each element, we skip `fibskip` elements, where `fibskip`
3578
    # is pulled from the Fibonacci sequence -- Fibonacci was chosen as a simple
3579
    # ~O(log(N)) algorithm that ensures we don't hit a common divisor of a dimension
3580
    # and only end up hashing one slice of the array (as might happen with powers of
3581
    # two). Finally, we find the next distinct value from the one we just hashed.
3582

3583
    # This is a little tricky since skipping an integer number of values inherently works
3584
    # with linear indices, but `findprev` uses `keys`. Hoist out the conversion "maps":
UNCOV
3585
    ks = keys(A)
×
UNCOV
3586
    key_to_linear = LinearIndices(ks) # Index into this map to compute the linear index
×
UNCOV
3587
    linear_to_key = vec(ks)           # And vice-versa
×
3588

3589
    # Start at the last index
UNCOV
3590
    keyidx = last(ks)
×
UNCOV
3591
    linidx = key_to_linear[keyidx]
×
UNCOV
3592
    fibskip = prevfibskip = oneunit(linidx)
×
UNCOV
3593
    first_linear = first(LinearIndices(linear_to_key))
×
UNCOV
3594
    n = 0
×
UNCOV
3595
    while true
×
UNCOV
3596
        n += 1
×
3597
        # Hash the element
UNCOV
3598
        elt = A[keyidx]
×
UNCOV
3599
        h = hash(keyidx=>elt, h)
×
3600

3601
        # Skip backwards a Fibonacci number of indices -- this is a linear index operation
UNCOV
3602
        linidx = key_to_linear[keyidx]
×
UNCOV
3603
        linidx < fibskip + first_linear && break
×
UNCOV
3604
        linidx -= fibskip
×
UNCOV
3605
        keyidx = linear_to_key[linidx]
×
3606

3607
        # Only increase the Fibonacci skip once every N iterations. This was chosen
3608
        # to be big enough that all elements of small arrays get hashed while
3609
        # obscenely large arrays are still tractable. With a choice of N=4096, an
3610
        # entirely-distinct 8000-element array will have ~75% of its elements hashed,
3611
        # with every other element hashed in the first half of the array. At the same
3612
        # time, hashing a `typemax(Int64)`-length Float64 range takes about a second.
UNCOV
3613
        if rem(n, 4096) == 0
×
UNCOV
3614
            fibskip, prevfibskip = fibskip + prevfibskip, fibskip
×
3615
        end
3616

3617
        # Find a key index with a value distinct from `elt` -- might be `keyidx` itself
UNCOV
3618
        keyidx = findprev(!isequal(elt), A, keyidx)
×
UNCOV
3619
        keyidx === nothing && break
×
UNCOV
3620
    end
×
3621

UNCOV
3622
    return h
×
3623
end
3624

3625
# The semantics of `collect` are weird. Better to write our own
UNCOV
3626
function rest(a::AbstractArray{T}, state...) where {T}
×
UNCOV
3627
    v = Vector{T}(undef, 0)
×
3628
    # assume only very few items are taken from the front
UNCOV
3629
    sizehint!(v, length(a))
×
UNCOV
3630
    return foldl(push!, Iterators.rest(a, state...), init=v)
×
3631
end
3632

3633

3634
## keepat! ##
3635

3636
# NOTE: since these use `@inbounds`, they are actually only intended for Vector and BitVector
3637

UNCOV
3638
function _keepat!(a::AbstractVector, inds)
×
UNCOV
3639
    local prev
×
UNCOV
3640
    i = firstindex(a)
×
UNCOV
3641
    for k in inds
×
UNCOV
3642
        if @isdefined(prev)
×
UNCOV
3643
            prev < k || throw(ArgumentError("indices must be unique and sorted"))
×
3644
        end
UNCOV
3645
        ak = a[k] # must happen even when i==k for bounds checking
×
UNCOV
3646
        if i != k
×
UNCOV
3647
            @inbounds a[i] = ak # k > i, so a[i] is inbounds
×
3648
        end
UNCOV
3649
        prev = k
×
UNCOV
3650
        i = nextind(a, i)
×
UNCOV
3651
    end
×
UNCOV
3652
    deleteat!(a, i:lastindex(a))
×
UNCOV
3653
    return a
×
3654
end
3655

UNCOV
3656
function _keepat!(a::AbstractVector, m::AbstractVector{Bool})
×
UNCOV
3657
    length(m) == length(a) || throw(BoundsError(a, m))
×
UNCOV
3658
    j = firstindex(a)
×
UNCOV
3659
    for i in eachindex(a, m)
×
UNCOV
3660
        @inbounds begin
×
UNCOV
3661
            if m[i]
×
UNCOV
3662
                i == j || (a[j] = a[i])
×
UNCOV
3663
                j = nextind(a, j)
×
3664
            end
3665
        end
UNCOV
3666
    end
×
UNCOV
3667
    deleteat!(a, j:lastindex(a))
×
3668
end
3669

3670
## 1-d circshift ##
UNCOV
3671
function circshift!(a::AbstractVector, shift::Integer)
×
UNCOV
3672
    n = length(a)
×
UNCOV
3673
    n == 0 && return a
×
UNCOV
3674
    shift = mod(shift, n)
×
UNCOV
3675
    shift == 0 && return a
×
UNCOV
3676
    l = lastindex(a)
×
UNCOV
3677
    reverse!(a, firstindex(a), l-shift)
×
UNCOV
3678
    reverse!(a, l-shift+1, lastindex(a))
×
UNCOV
3679
    reverse!(a)
×
UNCOV
3680
    return a
×
3681
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc