@@ -8,6 +8,7 @@ export eachindex
8
8
9
9
# Traits for linear indexing
10
10
linearindexing (:: BitArray ) = LinearFast ()
11
+ linearindexing {A<:BitArray} (:: Type{A} ) = LinearFast ()
11
12
12
13
# Iterator/state
13
14
abstract CartesianIndex{N} # the state for all multidimensional iterators
@@ -134,11 +135,7 @@ using .IteratorsMD
134
135
nothing
135
136
end
136
137
137
- unsafe_getindex (v:: Real , ind:: Int ) = v
138
- unsafe_getindex (v:: Range , ind:: Int ) = first (v) + (ind- 1 )* step (v)
139
138
unsafe_getindex (v:: BitArray , ind:: Int ) = Base. unsafe_bitgetindex (v. chunks, ind)
140
- unsafe_getindex (v:: AbstractArray , ind:: Int ) = v[ind]
141
- unsafe_getindex (v, ind:: Real ) = unsafe_getindex (v, to_index (ind))
142
139
143
140
unsafe_setindex! {T} (v:: AbstractArray{T} , x:: T , ind:: Int ) = (v[ind] = x; v)
144
141
unsafe_setindex! (v:: BitArray , x:: Bool , ind:: Int ) = (Base. unsafe_bitsetindex! (v. chunks, x, ind); v)
@@ -226,42 +223,115 @@ end
226
223
227
224
# ## subarray.jl
228
225
229
- # Here we want to skip creating the dict-based cached version,
230
- # so use the ngenerate function
231
- function gen_getindex_body (N:: Int )
226
+ function gen_setindex_body (N:: Int )
232
227
quote
233
- strd_1 = 1
234
- @nexprs $ N d-> (@inbounds strd_{d+ 1 } = strd_d* s. dims[d])
235
- ind -= 1
236
- indp = s. first_index
237
- @nexprs $ N d-> begin
238
- i = div (ind, strd_{$ N- d+ 1 })
239
- @inbounds indp += i* s. strides[$ N- d+ 1 ]
240
- ind -= i* strd_{$ N- d+ 1 }
228
+ Base. Cartesian. @nexprs $ N d-> (J_d = J[d])
229
+ Base. Cartesian. @ncall $ N checkbounds V J
230
+ Base. Cartesian. @nexprs $ N d-> (I_d = Base. to_index (J_d))
231
+ if ! isa (x, AbstractArray)
232
+ Base. Cartesian. @nloops $ N i d-> (1 : length (I_d)) d-> (@inbounds j_d = Base. unsafe_getindex (I_d, i_d)) begin
233
+ @inbounds (Base. Cartesian. @nref $ N V j) = x
234
+ end
235
+ else
236
+ X = x
237
+ Base. Cartesian. @ncall $ N Base. setindex_shape_check X I
238
+ k = 1
239
+ Base. Cartesian. @nloops $ N i d-> (1 : length (I_d)) d-> (@inbounds j_d = Base. unsafe_getindex (I_d, i_d)) begin
240
+ @inbounds (Base. Cartesian. @nref $ N V j) = X[k]
241
+ k += 1
242
+ end
241
243
end
242
- s . parent[indp]
244
+ V
243
245
end
244
246
end
245
247
246
- eval (ngenerate (:N , nothing , :(getindex {T} (s:: SubArray{T,N} , ind:: Integer )), gen_getindex_body, 2 : 5 , false ))
247
-
248
+ # # SubArray index merging
249
+ # A view created like V = A[2:3:8, 5:2:17] can later be indexed as V[2:7],
250
+ # creating a new 1d view.
251
+ # In such cases we have to collapse the 2d space spanned by the ranges.
252
+ #
253
+ # API:
254
+ # merge_indexes(V, indexes::NTuple, dims::Dims, linindex)
255
+ # where dims encodes the trailing sizes of the parent array,
256
+ # indexes encodes the view's trailing indexes into the parent array,
257
+ # and linindex encodes the subset of these elements that we'll select.
258
+ #
259
+ # The generic algorithm makes use of div to convert elements
260
+ # of linindex into a cartesian index into indexes, looks up
261
+ # the corresponding cartesian index into the parent, and then uses
262
+ # dims to convert back to a linear index into the parent array.
263
+ #
264
+ # However, a common case is linindex::UnitRange.
265
+ # Since div is slow and in(j::Int, linindex::UnitRange) is fast,
266
+ # it can be much faster to generate all possibilities and
267
+ # then test whether the corresponding linear index is in linindex.
268
+ # One exception occurs when only a small subset of the total
269
+ # is desired, in which case we fall back to the div-based algorithm.
270
+ stagedfunction merge_indexes (V, indexes:: NTuple , dims:: Dims , linindex:: UnitRange{Int} )
271
+ N = length (indexes)
272
+ N > 0 || error (" Cannot merge empty indexes" )
273
+ quote
274
+ n = length (linindex)
275
+ Base. Cartesian. @nexprs $ N d-> (I_d = indexes[d])
276
+ L = 1
277
+ dimoffset = ndims (V. parent) - length (dims)
278
+ Base. Cartesian. @nexprs $ N d-> (L *= dimsize (V. parent, d+ dimoffset, I_d))
279
+ if n < 0.1 L # this has not been carefully tuned
280
+ return merge_indexes_div (V, indexes, dims, linindex)
281
+ end
282
+ Pstride_1 = 1 # parent strides
283
+ Base. Cartesian. @nexprs $ (N- 1 ) d-> (Pstride_{d+ 1 } = Pstride_d* dims[d])
284
+ Istride_1 = 1 # indexes strides
285
+ Base. Cartesian. @nexprs $ (N- 1 ) d-> (Istride_{d+ 1 } = Istride_d* dimsize (V, d+ dimoffset, I_d))
286
+ Base. Cartesian. @nexprs $ N d-> (counter_d = 1 ) # counter_0 is a linear index into indexes
287
+ Base. Cartesian. @nexprs $ N d-> (offset_d = 1 ) # offset_0 is a linear index into parent
288
+ k = 0
289
+ index = Array (Int, n)
290
+ Base. Cartesian. @nloops $ N i d-> (1 : dimsize (V, d+ dimoffset, I_d)) d-> (offset_{d- 1 } = offset_d + (I_d[i_d]- 1 )* Pstride_d; counter_{d- 1 } = counter_d + (i_d- 1 )* Istride_d) begin
291
+ if in (counter_0, linindex)
292
+ index[k+= 1 ] = offset_0
293
+ end
294
+ end
295
+ index
296
+ end
297
+ end
298
+ merge_indexes (V, indexes:: NTuple , dims:: Dims , linindex) = merge_indexes_div (V, indexes, dims, linindex)
248
299
249
- function gen_setindex!_body (N:: Int )
300
+ # This could be written as a regular function, but performance
301
+ # will be better using Cartesian macros to avoid the heap and
302
+ # an extra loop.
303
+ stagedfunction merge_indexes_div (V, indexes:: NTuple , dims:: Dims , linindex)
304
+ N = length (indexes)
305
+ N > 0 || error (" Cannot merge empty indexes" )
306
+ Istride_N = symbol (" Istride_$N " )
250
307
quote
251
- strd_1 = 1
252
- @nexprs $ N d-> (@inbounds strd_{d+ 1 } = strd_d* s. dims[d])
253
- ind -= 1
254
- indp = s. first_index
255
- @nexprs $ N d-> begin
256
- i = div (ind, strd_{$ N- d+ 1 })
257
- @inbounds indp += i* s. strides[$ N- d+ 1 ]
258
- ind -= i* strd_{$ N- d+ 1 }
308
+ Base. Cartesian. @nexprs $ N d-> (I_d = indexes[d])
309
+ Pstride_1 = 1 # parent strides
310
+ Base. Cartesian. @nexprs $ (N- 1 ) d-> (Pstride_{d+ 1 } = Pstride_d* dims[d])
311
+ Istride_1 = 1 # indexes strides
312
+ dimoffset = ndims (V. parent) - length (dims)
313
+ Base. Cartesian. @nexprs $ (N- 1 ) d-> (Istride_{d+ 1 } = Istride_d* dimsize (V. parent, d+ dimoffset, I_d))
314
+ n = length (linindex)
315
+ L = $ (Istride_N) * dimsize (V. parent, $ N+ dimoffset, indexes[end ])
316
+ index = Array (Int, n)
317
+ for i = 1 : n
318
+ k = linindex[i] # k is the indexes-centered linear index
319
+ 1 <= k <= L || throw (BoundsError ())
320
+ k -= 1
321
+ j = 0 # j will be the new parent-centered linear index
322
+ Base. Cartesian. @nexprs $ N d-> (d < $ N ?
323
+ begin
324
+ c, k = divrem (k, Istride_{$ N- d+ 1 })
325
+ j += (Base. unsafe_getindex (I_{$ N- d+ 1 }, c+ 1 )- 1 )* Pstride_{$ N- d+ 1 }
326
+ end : begin
327
+ j += Base. unsafe_getindex (I_1, k+ 1 )
328
+ end )
329
+ index[i] = j
259
330
end
260
- s . parent[indp] = v
331
+ index
261
332
end
262
333
end
263
334
264
- eval (ngenerate (:N , nothing , :(setindex! {T} (s:: SubArray{T,N} , v, ind:: Integer )), gen_setindex!_body, 2 : 5 , false ))
265
335
266
336
cumsum (A:: AbstractArray , axis:: Integer = 1 ) = cumsum! (similar (A, Base. _cumsum_type (A)), A, axis)
267
337
cumprod (A:: AbstractArray , axis:: Integer = 1 ) = cumprod! (similar (A), A, axis)
@@ -622,7 +692,7 @@ for (V, PT, BT) in [((:N,), BitArray, BitArray), ((:T,:N), Array, StridedArray)]
622
692
offset = 1 - sum (@ntuple N d-> strides_{d+ 1 })
623
693
624
694
if isa (B, SubArray)
625
- offset += B . first_index - 1
695
+ offset += first_index (B) - 1
626
696
B = B. parent
627
697
end
628
698
0 commit comments