5
5
# This test file is designed to exercise support for generic indexing,
6
6
# even though offset arrays aren't implemented in Base.
7
7
8
- # OffsetArrays v1.11.2
8
+ # OffsetArrays v1.15.0
9
9
# No compat patch and docstrings
10
10
module OffsetArrays
11
11
73
73
IdOffsetRange (r:: IdOffsetRange ) = r
74
74
75
75
# Constructor to make `show` round-trippable
76
+ # try to preserve typeof(values) if the indices are known to be 1-based
77
+ _subtractindexoffset (values, indices:: Union{Base.OneTo, IdentityUnitRange{<:Base.OneTo}} , offset) = values
78
+ _subtractindexoffset (values, indices, offset) = _subtractoffset (values, offset)
76
79
function IdOffsetRange (; values:: AbstractUnitRange{<:Integer} , indices:: AbstractUnitRange{<:Integer} )
77
80
length (values) == length (indices) || throw (ArgumentError (" values and indices must have the same length" ))
81
+ values_nooffset = no_offset_view (values)
78
82
offset = first (indices) - 1
79
- return IdOffsetRange (values .- offset, offset)
83
+ values_minus_offset = _subtractindexoffset (values_nooffset, indices, offset)
84
+ return IdOffsetRange (values_minus_offset, offset)
80
85
end
81
86
82
87
# Conversions to an AbstractUnitRange{Int} (and to an OrdinalRange{Int,Int} on Julia v"1.6") are necessary
@@ -110,12 +115,19 @@ offset_coerce(::Type{I}, r::AbstractUnitRange) where I<:AbstractUnitRange =
110
115
@inline Base. unsafe_indices (r:: IdOffsetRange ) = (Base. axes1 (r),)
111
116
@inline Base. length (r:: IdOffsetRange ) = length (r. parent)
112
117
@inline Base. isempty (r:: IdOffsetRange ) = isempty (r. parent)
118
+ #= We specialize on reduced_indices to work around cases where the parent axis type doesn't
119
+ support reduced_index, but the axes do support reduced_indices
120
+ The difference is that reduced_index expects the axis type to remain unchanged,
121
+ which may not always be possible, eg. for statically sized axes
122
+ See https://github.com/JuliaArrays/OffsetArrays.jl/issues/204
123
+ =#
124
+ function Base. reduced_indices (inds:: Tuple{IdOffsetRange, Vararg{IdOffsetRange}} , d:: Int )
125
+ parents_reduced = Base. reduced_indices (map (parent, inds), d)
126
+ ntuple (i -> IdOffsetRange (parents_reduced[i], inds[i]. offset), Val (length (inds)))
127
+ end
113
128
Base. reduced_index (i:: IdOffsetRange ) = typeof (i)(first (i): first (i))
114
129
# Workaround for #92 on Julia < 1.4
115
130
Base. reduced_index (i:: IdentityUnitRange{<:IdOffsetRange} ) = typeof (i)(first (i): first (i))
116
- for f in [:firstindex , :lastindex ]
117
- @eval @inline Base.$ f (r:: IdOffsetRange ) = $ f (r. parent) + r. offset
118
- end
119
131
for f in [:first , :last ]
120
132
# coerce the type to deal with values that get promoted on addition (eg. Bool)
121
133
@eval @inline Base.$ f (r:: IdOffsetRange ) = eltype (r)($ f (r. parent) + r. offset)
142
154
@inline function Base. getindex (r:: IdOffsetRange , i:: Integer )
143
155
i isa Bool && throw (ArgumentError (" invalid index: $i of type Bool" ))
144
156
@boundscheck checkbounds (r, i)
145
- @inbounds eltype (r)(r. parent[oftype (r . offset, i) - r. offset] + r. offset)
157
+ @inbounds eltype (r)(r. parent[i - r. offset] + r. offset)
146
158
end
147
159
148
160
# Logical indexing following https://github.com/JuliaLang/julia/pull/31829
@@ -186,18 +198,20 @@ for R in [:IIUR, :IdOffsetRange]
186
198
end
187
199
188
200
# offset-preserve broadcasting
189
- Broadcast. broadcasted (:: Base.Broadcast.DefaultArrayStyle{1} , :: typeof (- ), r:: IdOffsetRange{T} , x:: Integer ) where T =
190
- IdOffsetRange {T} (r. parent .- x, r. offset)
191
- Broadcast. broadcasted (:: Base.Broadcast.DefaultArrayStyle{1} , :: typeof (+ ), r:: IdOffsetRange{T} , x:: Integer ) where T =
192
- IdOffsetRange {T} (r. parent .+ x, r. offset)
193
- Broadcast. broadcasted (:: Base.Broadcast.DefaultArrayStyle{1} , :: typeof (+ ), x:: Integer , r:: IdOffsetRange{T} ) where T =
194
- IdOffsetRange {T} (x .+ r. parent, r. offset)
201
+ Broadcast. broadcasted (:: Base.Broadcast.DefaultArrayStyle{1} , :: typeof (- ), r:: IdOffsetRange , x:: Integer ) =
202
+ IdOffsetRange (r. parent .- x, r. offset)
203
+ Broadcast. broadcasted (:: Base.Broadcast.DefaultArrayStyle{1} , :: typeof (+ ), r:: IdOffsetRange , x:: Integer ) =
204
+ IdOffsetRange (r. parent .+ x, r. offset)
205
+ Broadcast. broadcasted (:: Base.Broadcast.DefaultArrayStyle{1} , :: typeof (+ ), x:: Integer , r:: IdOffsetRange ) =
206
+ IdOffsetRange (x .+ r. parent, r. offset)
207
+ Broadcast. broadcasted (:: Base.Broadcast.DefaultArrayStyle{1} , :: typeof (big), r:: IdOffsetRange ) =
208
+ IdOffsetRange (big .(r. parent), r. offset)
195
209
196
210
Base. show (io:: IO , r:: IdOffsetRange ) = print (io, IdOffsetRange, " (values=" ,first (r), ' :' , last (r)," , indices=" ,first (eachindex (r)),' :' ,last (eachindex (r)), " )" )
197
211
198
212
# Optimizations
199
213
@inline Base. checkindex (:: Type{Bool} , inds:: IdOffsetRange , i:: Real ) = Base. checkindex (Bool, inds. parent, i - inds. offset)
200
- Base. _firstslice (r :: IdOffsetRange ) = IdOffsetRange (Base. _firstslice (r . parent), r . offset)
214
+ Base. _firstslice (i :: IdOffsetRange ) = IdOffsetRange (Base. _firstslice (i . parent), i . offset)
201
215
202
216
# #######################################################################################################
203
217
# origin.jl
@@ -309,12 +323,12 @@ _popreshape(A::AbstractArray, ax, inds) = A
309
323
310
324
# Technically we know the length of CartesianIndices but we need to convert it first, so here we
311
325
# don't put it in OffsetAxisKnownLength.
312
- const OffsetAxisKnownLength = Union{Integer,AbstractUnitRange}
313
- const OffsetAxis = Union{OffsetAxisKnownLength,Colon}
314
- const ArrayInitializer = Union{UndefInitializer,Missing,Nothing}
326
+ const OffsetAxisKnownLength = Union{Integer, AbstractUnitRange}
327
+ const OffsetAxis = Union{OffsetAxisKnownLength, Colon}
328
+ const ArrayInitializer = Union{UndefInitializer, Missing, Nothing}
315
329
316
330
# # OffsetArray
317
- struct OffsetArray{T,N,AA<: AbstractArray } <: AbstractArray{T,N}
331
+ struct OffsetArray{T,N,AA<: AbstractArray{T,N} } <: AbstractArray{T,N}
318
332
parent:: AA
319
333
offsets:: NTuple{N,Int}
320
334
@inline function OffsetArray {T, N, AA} (parent:: AA , offsets:: NTuple{N, Int} ; checkoverflow = true ) where {T, N, AA<: AbstractArray{T,N} }
@@ -482,6 +496,10 @@ Base.parent(A::OffsetArray) = A.parent
482
496
# Base.Broadcast.BroadcastStyle(::Type{<:OffsetArray{<:Any, <:Any, AA}}) where AA = Base.Broadcast.BroadcastStyle(AA)
483
497
484
498
@inline Base. size (A:: OffsetArray ) = size (parent (A))
499
+ # specializing length isn't necessary, as length(A) = prod(size(A)),
500
+ # but specializing length enables constant-propagation for statically sized arrays
501
+ # see https://github.com/JuliaArrays/OffsetArrays.jl/pull/304
502
+ @inline Base. length (A:: OffsetArray ) = length (parent (A))
485
503
486
504
@inline Base. axes (A:: OffsetArray ) = map (IdOffsetRange, axes (parent (A)), A. offsets)
487
505
@inline Base. axes (A:: OffsetArray , d) = d <= ndims (A) ? IdOffsetRange (axes (parent (A), d), A. offsets[d]) : IdOffsetRange (axes (parent (A), d))
@@ -528,7 +546,9 @@ _similar_axes_or_length(A, T, ax::I, ::I) where {I} = similar(A, T, map(_indexle
528
546
_similar_axes_or_length (AT, ax:: I , :: I ) where {I} = similar (AT, map (_indexlength, ax))
529
547
530
548
# reshape accepts a single colon
531
- Base. reshape (A:: AbstractArray , inds:: OffsetAxis... ) = reshape (A, inds)
549
+ # this method is limited to AbstractUnitRange{<:Integer} to avoid method overwritten errors if Base defines the same,
550
+ # see https://github.com/JuliaLang/julia/pull/56850
551
+ Base. reshape (A:: AbstractArray , inds:: Union{Integer, Colon, AbstractUnitRange{<:Integer}} ...) = reshape (A, inds)
532
552
function Base. reshape (A:: AbstractArray , inds:: Tuple{Vararg{OffsetAxis}} )
533
553
AR = reshape (no_offset_view (A), map (_indexlength, inds))
534
554
O = OffsetArray (AR, map (_offset, axes (AR), inds))
@@ -553,21 +573,9 @@ _reshape2(A, inds) = reshape(A, inds)
553
573
_reshape2 (A:: OffsetArray , inds) = reshape (parent (A), inds)
554
574
_reshape_nov (A, inds) = _reshape (no_offset_view (A), inds)
555
575
556
- Base. reshape (A:: OffsetArray , inds:: Tuple{OffsetAxis,Vararg{OffsetAxis}} ) =
557
- OffsetArray (_reshape (parent (A), inds), map (_toaxis, inds))
558
576
# And for non-offset axes, we can just return a reshape of the parent directly
559
- Base. reshape (A:: OffsetArray , inds:: Tuple{Union{Integer,Base.OneTo},Vararg{Union{Integer,Base.OneTo}}} ) = _reshape_nov (A, inds)
560
577
Base. reshape (A:: OffsetArray , inds:: Tuple{Integer,Vararg{Integer}} ) = _reshape_nov (A, inds)
561
- Base. reshape (A:: OffsetArray , inds:: Tuple{Union{Colon, Integer}, Vararg{Union{Colon, Integer}}} ) = _reshape_nov (A, inds)
562
578
Base. reshape (A:: OffsetArray , inds:: Dims ) = _reshape_nov (A, inds)
563
- Base. reshape (A:: OffsetVector , :: Colon ) = A
564
- Base. reshape (A:: OffsetVector , :: Tuple{Colon} ) = A
565
- Base. reshape (A:: OffsetArray , inds:: Union{Int,Colon} ...) = reshape (A, inds)
566
- Base. reshape (A:: OffsetArray , inds:: Tuple{Vararg{Union{Int,Colon}}} ) = _reshape_nov (A, inds)
567
- # The following two additional methods for Colon are added to resolve method ambiguities to
568
- # Base: https://github.com/JuliaLang/julia/pull/45387#issuecomment-1132859663
569
- Base. reshape (A:: OffsetArray , inds:: Colon ) = _reshape_nov (A, inds)
570
- Base. reshape (A:: OffsetArray , inds:: Tuple{Colon} ) = _reshape_nov (A, inds)
571
579
572
580
# permutedims in Base does not preserve axes, and can not be fixed in a non-breaking way
573
581
# This is a stopgap solution
@@ -583,7 +591,7 @@ Base.fill!(A::OffsetArray, x) = parent_call(Ap -> fill!(Ap, x), A)
583
591
# Δi = i - first(r)
584
592
# i′ = first(r.parent) + Δi
585
593
# and one obtains the result below.
586
- parentindex (r:: IdOffsetRange , i) = oftype (r . offset, i) - r. offset
594
+ parentindex (r:: IdOffsetRange , i) = i - r. offset
587
595
588
596
@propagate_inbounds Base. getindex (A:: OffsetArray{<:Any,0} ) = A. parent[]
589
597
@@ -632,7 +640,7 @@ Base.copy(A::OffsetArray) = parent_call(copy, A)
632
640
633
641
Base. strides (A:: OffsetArray ) = strides (parent (A))
634
642
Base. elsize (:: Type{OffsetArray{T,N,A}} ) where {T,N,A} = Base. elsize (A)
635
- Base. cconvert (:: Type{Ptr{T}} , A:: OffsetArray{T} ) where {T} = Base. cconvert (Ptr{T} , parent (A))
643
+ Base. cconvert (P :: Type{Ptr{T}} , A:: OffsetArray{T} ) where {T} = Base. cconvert (P , parent (A))
636
644
637
645
# For fast broadcasting: ref https://discourse.julialang.org/t/why-is-there-a-performance-hit-on-broadcasting-with-offsetarrays/32194
638
646
Base. dataids (A:: OffsetArray ) = Base. dataids (parent (A))
@@ -732,15 +740,6 @@ if eltype(IIUR) === Int
732
740
Base. map (:: Type{T} , r:: IdentityUnitRange ) where {T<: Real } = _indexedby (map (T, UnitRange (r)), axes (r))
733
741
end
734
742
735
- # mapreduce is faster with an IdOffsetRange than with an OffsetUnitRange
736
- # We therefore convert OffsetUnitRanges to IdOffsetRanges with the same values and axes
737
- function Base. mapreduce (f, op, A1:: OffsetUnitRange{<:Integer} , As:: OffsetUnitRange{<:Integer} ...; kw... )
738
- As = (A1, As... )
739
- ofs = map (A -> first (axes (A,1 )) - 1 , As)
740
- AIds = map ((A, of) -> IdOffsetRange (_subtractoffset (parent (A), of), of), As, ofs)
741
- mapreduce (f, op, AIds... ; kw... )
742
- end
743
-
744
743
# Optimize certain reductions that treat an OffsetVector as a list
745
744
for f in [:minimum , :maximum , :extrema , :sum ]
746
745
@eval Base.$ f (r:: OffsetRange ) = $ f (parent (r))
@@ -762,7 +761,8 @@ Base.append!(A::OffsetVector, items) = (append!(A.parent, items); A)
762
761
Base. empty! (A:: OffsetVector ) = (empty! (A. parent); A)
763
762
764
763
# These functions keep the summary compact
765
- function Base. inds2string (inds:: Tuple {Vararg{Union{IdOffsetRange, IdentityUnitRange{<: IdOffsetRange }}}})
764
+ const OffsetIndices = Union{IdOffsetRange, IdentityUnitRange{<: IdOffsetRange }}
765
+ function Base. inds2string (inds:: Tuple{OffsetIndices, Vararg{OffsetIndices}} )
766
766
Base. inds2string (map (UnitRange, inds))
767
767
end
768
768
Base. showindices (io:: IO , ind1:: IdOffsetRange , inds:: IdOffsetRange... ) = Base. showindices (io, map (UnitRange, (ind1, inds... ))... )
@@ -786,7 +786,33 @@ function Base.replace_in_print_matrix(A::OffsetArray{<:Any,1}, i::Integer, j::In
786
786
Base. replace_in_print_matrix (parent (A), ip, j, s)
787
787
end
788
788
789
+ # Actual unsafe_wrap implementation
790
+ @inline function _unsafe_wrap (pointer:: Ptr{T} , inds:: NTuple{N, OffsetAxisKnownLength} ; own = false , kw... ) where {T,N}
791
+ _checkindices (N, inds, " indices" )
792
+ AA = Base. unsafe_wrap (Array, pointer, map (_indexlength, inds); own= own)
793
+ OffsetArray {T, N, typeof(AA)} (AA, map (_indexoffset, inds); kw... )
794
+ end
795
+ const OffsetArrayUnion{T,N} = Union{Type{OffsetArray}, Type{OffsetArray{T}}, Type{OffsetArray{T,N}}, Type{OffsetArray{T1, N} where T1}} where {T,N}
796
+
797
+ @inline function Base. unsafe_wrap (:: OffsetArrayUnion{T,N} , pointer:: Ptr{T} , inds:: NTuple{N, OffsetAxisKnownLength} ; kw... ) where {T,N}
798
+ _unsafe_wrap (pointer, inds; kw... )
799
+ end
800
+ # Avoid ambiguity
801
+ @inline function Base. unsafe_wrap (:: OffsetArrayUnion{T,N} , pointer:: Ptr{T} , inds:: NTuple{N, <:Integer} ; kw... ) where {T,N}
802
+ _unsafe_wrap (pointer, inds; kw... )
803
+ end
804
+ @inline function Base. unsafe_wrap (:: OffsetArrayUnion{T,N} , pointer:: Ptr{T} , inds:: Vararg{OffsetAxisKnownLength,N} ; kw... ) where {T,N}
805
+ _unsafe_wrap (pointer, inds; kw... )
806
+ end
807
+ # Avoid ambiguity
808
+ @inline function Base. unsafe_wrap (:: OffsetArrayUnion{T,N} , pointer:: Ptr{T} , inds:: Vararg{Integer,N} ; kw... ) where {T,N}
809
+ _unsafe_wrap (pointer, inds; kw... )
810
+ end
811
+
789
812
no_offset_view (A:: OffsetArray ) = no_offset_view (parent (A))
813
+ no_offset_view (a:: Base.Slice{<:Base.OneTo} ) = a
814
+ no_offset_view (a:: Base.Slice ) = Base. Slice (UnitRange (a))
815
+ no_offset_view (S:: SubArray ) = view (parent (S), map (no_offset_view, parentindices (S))... )
790
816
no_offset_view (a:: Array ) = a
791
817
no_offset_view (i:: Number ) = i
792
818
no_offset_view (A:: AbstractArray ) = _no_offset_view (axes (A), A)
@@ -802,9 +828,12 @@ _no_offset_view(::Any, A::AbstractUnitRange) = UnitRange(A)
802
828
# These two helpers are deliberately not exported; their meaning can be very different in
803
829
# other scenarios and will be very likely to cause name conflicts if exported.
804
830
# ####
831
+
832
+ _halfroundInt (v, r:: RoundingMode ) = div (v, 2 , r)
833
+
805
834
function center (A:: AbstractArray , r:: RoundingMode = RoundDown)
806
835
map (axes (A)) do inds
807
- round (Int, ( length (inds)- 1 ) / 2 , r) + first (inds)
836
+ _halfroundInt ( length (inds)- 1 , r) + first (inds)
808
837
end
809
838
end
810
839
0 commit comments