@@ -11,6 +11,7 @@ module IteratorsMD
11
11
using . Base: IndexLinear, IndexCartesian, AbstractCartesianIndex, fill_to_length, tail,
12
12
ReshapedArray, ReshapedArrayLF, OneTo
13
13
using . Base. Iterators: Reverse, PartitionIterator
14
+ using . Base: @propagate_inbounds
14
15
15
16
export CartesianIndex, CartesianIndices
16
17
@@ -149,13 +150,13 @@ module IteratorsMD
149
150
function Base. nextind (a:: AbstractArray{<:Any,N} , i:: CartesianIndex{N} ) where {N}
150
151
iter = CartesianIndices (axes (a))
151
152
# might overflow
152
- I = inc (i. I, first ( iter) . I, last (iter) . I )
153
+ I = inc (i. I, iter. indices )
153
154
return I
154
155
end
155
156
function Base. prevind (a:: AbstractArray{<:Any,N} , i:: CartesianIndex{N} ) where {N}
156
157
iter = CartesianIndices (axes (a))
157
158
# might underflow
158
- I = dec (i. I, last ( iter) . I, first (iter) . I )
159
+ I = dec (i. I, iter. indices )
159
160
return I
160
161
end
161
162
@@ -169,15 +170,15 @@ module IteratorsMD
169
170
# Iteration
170
171
"""
171
172
CartesianIndices(sz::Dims) -> R
172
- CartesianIndices((istart:istop, jstart:jstop, ...)) -> R
173
+ CartesianIndices((istart:[istep:] istop, jstart:[jstep:] jstop, ...)) -> R
173
174
174
175
Define a region `R` spanning a multidimensional rectangular range
175
176
of integer indices. These are most commonly encountered in the
176
177
context of iteration, where `for I in R ... end` will return
177
178
[`CartesianIndex`](@ref) indices `I` equivalent to the nested loops
178
179
179
- for j = jstart:jstop
180
- for i = istart:istop
180
+ for j = jstart:jstep: jstop
181
+ for i = istart:istep: istop
181
182
...
182
183
end
183
184
end
@@ -190,6 +191,10 @@ module IteratorsMD
190
191
As a convenience, constructing a `CartesianIndices` from an array makes a
191
192
range of its indices.
192
193
194
+ !!! compat "Julia 1.6"
195
+ The step range method `CartesianIndices((istart:istep:istop, jstart:[jstep:]jstop, ...))`
196
+ requires at least Julia 1.6.
197
+
193
198
# Examples
194
199
```jldoctest
195
200
julia> foreach(println, CartesianIndices((2, 2, 2)))
@@ -222,6 +227,15 @@ module IteratorsMD
222
227
223
228
julia> cartesian[4]
224
229
CartesianIndex(1, 2)
230
+
231
+ julia> cartesian = CartesianIndices((1:2:5, 1:2))
232
+ 3×2 CartesianIndices{2, Tuple{StepRange{Int64, Int64}, UnitRange{Int64}}}:
233
+ CartesianIndex(1, 1) CartesianIndex(1, 2)
234
+ CartesianIndex(3, 1) CartesianIndex(3, 2)
235
+ CartesianIndex(5, 1) CartesianIndex(5, 2)
236
+
237
+ julia> cartesian[2, 2]
238
+ CartesianIndex(3, 2)
225
239
```
226
240
227
241
## Broadcasting
@@ -248,29 +262,37 @@ module IteratorsMD
248
262
249
263
For cartesian to linear index conversion, see [`LinearIndices`](@ref).
250
264
"""
251
- struct CartesianIndices{N,R<: NTuple{N,AbstractUnitRange{ Int}} } <: AbstractArray{CartesianIndex{N},N}
265
+ struct CartesianIndices{N,R<: NTuple{N,OrdinalRange{Int, Int}} } <: AbstractArray{CartesianIndex{N},N}
252
266
indices:: R
253
267
end
254
268
255
269
CartesianIndices (:: Tuple{} ) = CartesianIndices {0,typeof(())} (())
256
- CartesianIndices (inds:: NTuple{N,AbstractUnitRange{<:Integer}} ) where {N} =
257
- CartesianIndices (map (r-> convert (AbstractUnitRange{Int}, r), inds))
270
+ function CartesianIndices (inds:: NTuple{N,OrdinalRange{<:Integer, <:Integer}} ) where {N}
271
+ indices = map (r-> convert (OrdinalRange{Int, Int}, r), inds)
272
+ CartesianIndices {N, typeof(indices)} (indices)
273
+ end
258
274
259
275
CartesianIndices (index:: CartesianIndex ) = CartesianIndices (index. I)
260
- CartesianIndices (sz:: NTuple{N,<:Integer} ) where {N} = CartesianIndices (map (Base. OneTo, sz))
261
- CartesianIndices (inds:: NTuple{N,Union{<:Integer,AbstractUnitRange{<:Integer}}} ) where {N} =
262
- CartesianIndices (map (i-> first (i): last (i), inds))
276
+ CartesianIndices (inds:: NTuple{N,Union{<:Integer,OrdinalRange{<:Integer}}} ) where {N} =
277
+ CartesianIndices (map (_convert2ind, inds))
263
278
264
279
CartesianIndices (A:: AbstractArray ) = CartesianIndices (axes (A))
265
280
281
+ _convert2ind (sz:: Integer ) = Base. OneTo (sz)
282
+ _convert2ind (sz:: AbstractUnitRange ) = first (sz): last (sz)
283
+ _convert2ind (sz:: OrdinalRange ) = first (sz): step (sz): last (sz)
284
+
266
285
"""
267
- (:)(I ::CartesianIndex, J ::CartesianIndex)
286
+ (:)(start ::CartesianIndex, [step::CartesianIndex], stop ::CartesianIndex)
268
287
269
- Construct [`CartesianIndices`](@ref) from two `CartesianIndex`.
288
+ Construct [`CartesianIndices`](@ref) from two `CartesianIndex` and an optional step .
270
289
271
290
!!! compat "Julia 1.1"
272
291
This method requires at least Julia 1.1.
273
292
293
+ !!! compat "Julia 1.6"
294
+ The step range method start:step:stop requires at least Julia 1.6.
295
+
274
296
# Examples
275
297
```jldoctest
276
298
julia> I = CartesianIndex(2,1);
@@ -281,17 +303,26 @@ module IteratorsMD
281
303
2×3 CartesianIndices{2, Tuple{UnitRange{Int64}, UnitRange{Int64}}}:
282
304
CartesianIndex(2, 1) CartesianIndex(2, 2) CartesianIndex(2, 3)
283
305
CartesianIndex(3, 1) CartesianIndex(3, 2) CartesianIndex(3, 3)
306
+
307
+ julia> I:CartesianIndex(1, 2):J
308
+ 2×2 CartesianIndices{2, Tuple{StepRange{Int64, Int64}, StepRange{Int64, Int64}}}:
309
+ CartesianIndex(2, 1) CartesianIndex(2, 3)
310
+ CartesianIndex(3, 1) CartesianIndex(3, 3)
284
311
```
285
312
"""
286
313
(:)(I:: CartesianIndex{N} , J:: CartesianIndex{N} ) where N =
287
314
CartesianIndices (map ((i,j) -> i: j, Tuple (I), Tuple (J)))
315
+ (:)(I:: CartesianIndex{N} , S:: CartesianIndex{N} , J:: CartesianIndex{N} ) where N =
316
+ CartesianIndices (map ((i,s,j) -> i: s: j, Tuple (I), Tuple (S), Tuple (J)))
288
317
289
318
promote_rule (:: Type{CartesianIndices{N,R1}} , :: Type{CartesianIndices{N,R2}} ) where {N,R1,R2} =
290
319
CartesianIndices{N,Base. indices_promote_type (R1,R2)}
291
320
292
321
convert (:: Type{Tuple{}} , R:: CartesianIndices{0} ) = ()
293
- convert (:: Type{NTuple{N,AbstractUnitRange{Int}}} , R:: CartesianIndices{N} ) where {N} =
294
- R. indices
322
+ for RT in (OrdinalRange{Int, Int}, StepRange{Int, Int}, AbstractUnitRange{Int})
323
+ @eval convert (:: Type{NTuple{N,$RT}} , R:: CartesianIndices{N} ) where {N} =
324
+ map (x-> convert ($ RT, x), R. indices)
325
+ end
295
326
convert (:: Type{NTuple{N,AbstractUnitRange}} , R:: CartesianIndices{N} ) where {N} =
296
327
convert (NTuple{N,AbstractUnitRange{Int}}, R)
297
328
convert (:: Type{NTuple{N,UnitRange{Int}}} , R:: CartesianIndices{N} ) where {N} =
@@ -318,13 +349,8 @@ module IteratorsMD
318
349
# AbstractArray implementation
319
350
Base. axes (iter:: CartesianIndices{N,R} ) where {N,R} = map (Base. axes1, iter. indices)
320
351
Base. IndexStyle (:: Type{CartesianIndices{N,R}} ) where {N,R} = IndexCartesian ()
321
- @inline function Base. getindex (iter:: CartesianIndices{N,<:NTuple{N,Base.OneTo}} , I:: Vararg{Int, N} ) where {N}
322
- @boundscheck checkbounds (iter, I... )
323
- CartesianIndex (I)
324
- end
325
- @inline function Base. getindex (iter:: CartesianIndices{N,R} , I:: Vararg{Int, N} ) where {N,R}
326
- @boundscheck checkbounds (iter, I... )
327
- CartesianIndex (I .- first .(Base. axes1 .(iter. indices)) .+ first .(iter. indices))
352
+ @propagate_inbounds function Base. getindex (iter:: CartesianIndices{N,R} , I:: Vararg{Int, N} ) where {N,R}
353
+ CartesianIndex (getindex .(iter. indices, I))
328
354
end
329
355
330
356
ndims (R:: CartesianIndices ) = ndims (typeof (R))
@@ -344,47 +370,65 @@ module IteratorsMD
344
370
IteratorSize (:: Type{<:CartesianIndices{N}} ) where {N} = Base. HasShape {N} ()
345
371
346
372
@inline function iterate (iter:: CartesianIndices )
347
- iterfirst, iterlast = first (iter), last (iter)
348
- if any (map (> , iterfirst. I, iterlast . I ))
373
+ iterfirst = first (iter)
374
+ if ! all (map (in , iterfirst. I, iter . indices ))
349
375
return nothing
350
376
end
351
377
iterfirst, iterfirst
352
378
end
353
379
@inline function iterate (iter:: CartesianIndices , state)
354
- valid, I = __inc (state. I, first ( iter) . I, last (iter) . I )
380
+ valid, I = __inc (state. I, iter. indices )
355
381
valid || return nothing
356
382
return CartesianIndex (I... ), CartesianIndex (I... )
357
383
end
358
384
359
385
# increment & carry
360
- @inline function inc (state, start, stop )
361
- _, I = __inc (state, start, stop )
386
+ @inline function inc (state, indices )
387
+ _, I = __inc (state, indices )
362
388
return CartesianIndex (I... )
363
389
end
364
390
365
- # increment post check to avoid integer overflow
366
- @inline __inc (:: Tuple{} , :: Tuple{} , :: Tuple{} ) = false , ()
367
- @inline function __inc (state:: Tuple{Int} , start:: Tuple{Int} , stop:: Tuple{Int} )
368
- valid = state[1 ] < stop[1 ]
369
- return valid, (state[1 ]+ 1 ,)
391
+ # Unlike ordinary ranges, CartesianIndices continues the iteration in the next column when the
392
+ # current column is consumed. The implementation is written recursively to achieve this.
393
+ # `iterate` returns `Union{Nothing, Tuple}`, we explicitly pass a `valid` flag to eliminate
394
+ # the type instability inside the core `__inc` logic, and this gives better runtime performance.
395
+ __inc (:: Tuple{} , :: Tuple{} ) = false , ()
396
+ @inline function __inc (state:: Tuple{Int} , indices:: Tuple{<:OrdinalRange} )
397
+ rng = indices[1 ]
398
+ I = state[1 ] + step (rng)
399
+ valid = __is_valid_range (I, rng) && state[1 ] != last (rng)
400
+ return valid, (I, )
401
+ end
402
+ @inline function __inc (state, indices)
403
+ rng = indices[1 ]
404
+ I = state[1 ] + step (rng)
405
+ if __is_valid_range (I, rng) && state[1 ] != last (rng)
406
+ return true , (I, tail (state)... )
407
+ end
408
+ valid, I = __inc (tail (state), tail (indices))
409
+ return valid, (first (rng), I... )
370
410
end
371
411
372
- @inline function __inc (state, start, stop)
373
- if state[1 ] < stop[1 ]
374
- return true , (state[1 ]+ 1 , tail (state)... )
412
+ @inline __is_valid_range (I, rng:: AbstractUnitRange ) = I in rng
413
+ @inline function __is_valid_range (I, rng:: OrdinalRange )
414
+ if step (rng) > 0
415
+ lo, hi = first (rng), last (rng)
416
+ else
417
+ lo, hi = last (rng), first (rng)
375
418
end
376
- valid, I = __inc (tail (state), tail (start), tail (stop))
377
- return valid, (start[1 ], I... )
419
+ lo <= I <= hi
378
420
end
379
421
380
422
# 0-d cartesian ranges are special-cased to iterate once and only once
381
423
iterate (iter:: CartesianIndices{0} , done= false ) = done ? nothing : (CartesianIndex (), true )
382
424
383
- size (iter:: CartesianIndices ) = map (dimlength, first (iter). I, last (iter). I)
384
- dimlength (start, stop) = stop- start+ 1
425
+ size (iter:: CartesianIndices ) = map (length, iter. indices)
385
426
386
427
length (iter:: CartesianIndices ) = prod (size (iter))
387
428
429
+ # make CartesianIndices a multidimensional range
430
+ Base. step (iter:: CartesianIndices ) = CartesianIndex (map (step, iter. indices))
431
+
388
432
first (iter:: CartesianIndices ) = CartesianIndex (map (first, iter. indices))
389
433
last (iter:: CartesianIndices ) = CartesianIndex (map (last, iter. indices))
390
434
@@ -395,11 +439,8 @@ module IteratorsMD
395
439
@inline to_indices (A, inds, I:: Tuple{CartesianIndices{0},Vararg{Any}} ) =
396
440
(first (I), to_indices (A, inds, tail (I))... )
397
441
398
- @inline function in (i:: CartesianIndex{N} , r:: CartesianIndices{N} ) where {N}
399
- _in (true , i. I, first (r). I, last (r). I)
400
- end
401
- _in (b, :: Tuple{} , :: Tuple{} , :: Tuple{} ) = b
402
- @inline _in (b, i, start, stop) = _in (b & (start[1 ] <= i[1 ] <= stop[1 ]), tail (i), tail (start), tail (stop))
442
+ @inline in (i:: CartesianIndex , r:: CartesianIndices ) = false
443
+ @inline in (i:: CartesianIndex{N} , r:: CartesianIndices{N} ) where {N} = all (map (in, i. I, r. indices))
403
444
404
445
simd_outer_range (iter:: CartesianIndices{0} ) = iter
405
446
function simd_outer_range (iter:: CartesianIndices )
@@ -410,8 +451,8 @@ module IteratorsMD
410
451
simd_inner_length (iter:: CartesianIndices , I:: CartesianIndex ) = Base. length (iter. indices[1 ])
411
452
412
453
simd_index (iter:: CartesianIndices{0} , :: CartesianIndex , I1:: Int ) = first (iter)
413
- @inline function simd_index (iter:: CartesianIndices , Ilast:: CartesianIndex , I1:: Int )
414
- CartesianIndex (( I1+ first (iter. indices[1 ]), Ilast. I... ) )
454
+ @propagate_inbounds function simd_index (iter:: CartesianIndices , Ilast:: CartesianIndex , I1:: Int )
455
+ CartesianIndex (getindex (iter . indices[ 1 ], I1+ first (Base . axes1 ( iter. indices[1 ]))) , Ilast. I... )
415
456
end
416
457
417
458
# Split out the first N elements of a tuple
@@ -440,44 +481,79 @@ module IteratorsMD
440
481
441
482
# reversed CartesianIndices iteration
442
483
484
+ Base. reverse (iter:: CartesianIndices ) = CartesianIndices (reverse .(iter. indices))
485
+
443
486
@inline function iterate (r:: Reverse{<:CartesianIndices} )
444
- iterfirst, iterlast = last (r . itr), first (r. itr)
445
- if any (map (< , iterfirst. I, iterlast . I ))
487
+ iterfirst = last (r. itr)
488
+ if ! all (map (in , iterfirst. I, r . itr . indices ))
446
489
return nothing
447
490
end
448
491
iterfirst, iterfirst
449
492
end
450
493
@inline function iterate (r:: Reverse{<:CartesianIndices} , state)
451
- valid, I = __dec (state. I, last ( r. itr) . I, first (r . itr) . I )
494
+ valid, I = __dec (state. I, r. itr. indices )
452
495
valid || return nothing
453
496
return CartesianIndex (I... ), CartesianIndex (I... )
454
497
end
455
498
456
499
# decrement & carry
457
- @inline function dec (state, start, stop )
458
- _, I = __dec (state, start, stop )
500
+ @inline function dec (state, indices )
501
+ _, I = __dec (state, indices )
459
502
return CartesianIndex (I... )
460
503
end
461
504
462
505
# decrement post check to avoid integer overflow
463
- @inline __dec (:: Tuple{} , :: Tuple{} , :: Tuple{} ) = false , ()
464
- @inline function __dec (state:: Tuple{Int} , start:: Tuple{Int} , stop:: Tuple{Int} )
465
- valid = state[1 ] > stop[1 ]
466
- return valid, (state[1 ]- 1 ,)
506
+ @inline __dec (:: Tuple{} , :: Tuple{} ) = false , ()
507
+ @inline function __dec (state:: Tuple{Int} , indices:: Tuple{<:OrdinalRange} )
508
+ rng = indices[1 ]
509
+ I = state[1 ] - step (rng)
510
+ valid = __is_valid_range (I, rng) && state[1 ] != first (rng)
511
+ return valid, (I,)
467
512
end
468
513
469
- @inline function __dec (state, start, stop)
470
- if state[1 ] > stop[1 ]
471
- return true , (state[1 ]- 1 , tail (state)... )
514
+ @inline function __dec (state, indices)
515
+ rng = indices[1 ]
516
+ I = state[1 ] - step (rng)
517
+ if __is_valid_range (I, rng) && state[1 ] != first (rng)
518
+ return true , (I, tail (state)... )
472
519
end
473
- valid, I = __dec (tail (state), tail (start), tail (stop ))
474
- return valid, (start[ 1 ] , I... )
520
+ valid, I = __dec (tail (state), tail (indices ))
521
+ return valid, (last (rng) , I... )
475
522
end
476
523
477
524
# 0-d cartesian ranges are special-cased to iterate once and only once
478
525
iterate (iter:: Reverse{<:CartesianIndices{0}} , state= false ) = state ? nothing : (CartesianIndex (), true )
479
526
480
- Base. LinearIndices (inds:: CartesianIndices{N,R} ) where {N,R} = LinearIndices {N,R} (inds. indices)
527
+ function Base. LinearIndices (inds:: CartesianIndices{N,R} ) where {N,R<: NTuple{N, AbstractUnitRange} }
528
+ LinearIndices {N,R} (inds. indices)
529
+ end
530
+ function Base. LinearIndices (inds:: CartesianIndices )
531
+ indices = inds. indices
532
+ if all (x-> step (x)== 1 , indices)
533
+ indices = map (rng-> first (rng): last (rng), indices)
534
+ LinearIndices {length(indices), typeof(indices)} (indices)
535
+ else
536
+ # Given the fact that StepRange 1:2:4 === 1:2:3, we lost the original size information
537
+ # and thus cannot calculate the correct linear indices when the steps are not 1.
538
+ throw (ArgumentError (" LinearIndices for $(typeof (inds)) with non-1 step size is not yet supported." ))
539
+ end
540
+ end
541
+
542
+ # This is currently needed because converting to LinearIndices is only available when steps are
543
+ # all 1
544
+ # NOTE: this is only a temporary patch and could be possibly removed when StepRange support to
545
+ # LinearIndices is done
546
+ function Base. collect (inds:: CartesianIndices{N, R} ) where {N,R<: NTuple{N, AbstractUnitRange} }
547
+ Base. _collect_indices (axes (inds), inds)
548
+ end
549
+ function Base. collect (inds:: CartesianIndices )
550
+ dest = Array {eltype(inds), ndims(inds)} (undef, size (inds))
551
+ i = 0
552
+ @inbounds for a in inds
553
+ dest[i+= 1 ] = a
554
+ end
555
+ dest
556
+ end
481
557
482
558
# array operations
483
559
Base. intersect (a:: CartesianIndices{N} , b:: CartesianIndices{N} ) where N =
@@ -501,7 +577,7 @@ module IteratorsMD
501
577
end
502
578
@inline function iterate (iter:: CartesianPartition , (state, n))
503
579
n >= length (iter) && return nothing
504
- I = IteratorsMD. inc (state. I, first ( iter. parent. parent) . I, last (iter . parent . parent) . I )
580
+ I = IteratorsMD. inc (state. I, iter. parent. parent. indices )
505
581
return I, (I, n+ 1 )
506
582
end
507
583
0 commit comments