55# This test file is designed to exercise support for generic indexing,
66# even though offset arrays aren't implemented in Base.
77
8- # OffsetArrays v1.11.2
8+ # OffsetArrays v1.15.0
99# No compat patch and docstrings
1010module OffsetArrays
1111
7373IdOffsetRange (r:: IdOffsetRange ) = r
7474
7575# 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)
7679function IdOffsetRange (; values:: AbstractUnitRange{<:Integer} , indices:: AbstractUnitRange{<:Integer} )
7780 length (values) == length (indices) || throw (ArgumentError (" values and indices must have the same length" ))
81+ values_nooffset = no_offset_view (values)
7882 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)
8085end
8186
8287# 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 =
110115@inline Base. unsafe_indices (r:: IdOffsetRange ) = (Base. axes1 (r),)
111116@inline Base. length (r:: IdOffsetRange ) = length (r. parent)
112117@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
113128Base. reduced_index (i:: IdOffsetRange ) = typeof (i)(first (i): first (i))
114129# Workaround for #92 on Julia < 1.4
115130Base. 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
119131for f in [:first , :last ]
120132 # coerce the type to deal with values that get promoted on addition (eg. Bool)
121133 @eval @inline Base.$ f (r:: IdOffsetRange ) = eltype (r)($ f (r. parent) + r. offset)
142154@inline function Base. getindex (r:: IdOffsetRange , i:: Integer )
143155 i isa Bool && throw (ArgumentError (" invalid index: $i of type Bool" ))
144156 @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)
146158end
147159
148160# Logical indexing following https://github.com/JuliaLang/julia/pull/31829
@@ -186,18 +198,20 @@ for R in [:IIUR, :IdOffsetRange]
186198end
187199
188200# 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)
195209
196210Base. show (io:: IO , r:: IdOffsetRange ) = print (io, IdOffsetRange, " (values=" ,first (r), ' :' , last (r)," , indices=" ,first (eachindex (r)),' :' ,last (eachindex (r)), " )" )
197211
198212# Optimizations
199213@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)
201215
202216# #######################################################################################################
203217# origin.jl
@@ -309,12 +323,12 @@ _popreshape(A::AbstractArray, ax, inds) = A
309323
310324# Technically we know the length of CartesianIndices but we need to convert it first, so here we
311325# 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}
315329
316330# # OffsetArray
317- struct OffsetArray{T,N,AA<: AbstractArray } <: AbstractArray{T,N}
331+ struct OffsetArray{T,N,AA<: AbstractArray{T,N} } <: AbstractArray{T,N}
318332 parent:: AA
319333 offsets:: NTuple{N,Int}
320334 @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
482496# Base.Broadcast.BroadcastStyle(::Type{<:OffsetArray{<:Any, <:Any, AA}}) where AA = Base.Broadcast.BroadcastStyle(AA)
483497
484498@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))
485503
486504@inline Base. axes (A:: OffsetArray ) = map (IdOffsetRange, axes (parent (A)), A. offsets)
487505@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
528546_similar_axes_or_length (AT, ax:: I , :: I ) where {I} = similar (AT, map (_indexlength, ax))
529547
530548# 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)
532552function Base. reshape (A:: AbstractArray , inds:: Tuple{Vararg{OffsetAxis}} )
533553 AR = reshape (no_offset_view (A), map (_indexlength, inds))
534554 O = OffsetArray (AR, map (_offset, axes (AR), inds))
@@ -553,21 +573,9 @@ _reshape2(A, inds) = reshape(A, inds)
553573_reshape2 (A:: OffsetArray , inds) = reshape (parent (A), inds)
554574_reshape_nov (A, inds) = _reshape (no_offset_view (A), inds)
555575
556- Base. reshape (A:: OffsetArray , inds:: Tuple{OffsetAxis,Vararg{OffsetAxis}} ) =
557- OffsetArray (_reshape (parent (A), inds), map (_toaxis, inds))
558576# 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)
560577Base. 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)
562578Base. 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)
571579
572580# permutedims in Base does not preserve axes, and can not be fixed in a non-breaking way
573581# This is a stopgap solution
@@ -583,7 +591,7 @@ Base.fill!(A::OffsetArray, x) = parent_call(Ap -> fill!(Ap, x), A)
583591# Δi = i - first(r)
584592# i′ = first(r.parent) + Δi
585593# 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
587595
588596@propagate_inbounds Base. getindex (A:: OffsetArray{<:Any,0} ) = A. parent[]
589597
@@ -632,7 +640,7 @@ Base.copy(A::OffsetArray) = parent_call(copy, A)
632640
633641Base. strides (A:: OffsetArray ) = strides (parent (A))
634642Base. 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))
636644
637645# For fast broadcasting: ref https://discourse.julialang.org/t/why-is-there-a-performance-hit-on-broadcasting-with-offsetarrays/32194
638646Base. dataids (A:: OffsetArray ) = Base. dataids (parent (A))
@@ -732,15 +740,6 @@ if eltype(IIUR) === Int
732740 Base. map (:: Type{T} , r:: IdentityUnitRange ) where {T<: Real } = _indexedby (map (T, UnitRange (r)), axes (r))
733741end
734742
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-
744743# Optimize certain reductions that treat an OffsetVector as a list
745744for f in [:minimum , :maximum , :extrema , :sum ]
746745 @eval Base.$ f (r:: OffsetRange ) = $ f (parent (r))
@@ -762,7 +761,8 @@ Base.append!(A::OffsetVector, items) = (append!(A.parent, items); A)
762761Base. empty! (A:: OffsetVector ) = (empty! (A. parent); A)
763762
764763# 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}} )
766766 Base. inds2string (map (UnitRange, inds))
767767end
768768Base. 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
786786 Base. replace_in_print_matrix (parent (A), ip, j, s)
787787end
788788
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+
789812no_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))... )
790816no_offset_view (a:: Array ) = a
791817no_offset_view (i:: Number ) = i
792818no_offset_view (A:: AbstractArray ) = _no_offset_view (axes (A), A)
@@ -802,9 +828,12 @@ _no_offset_view(::Any, A::AbstractUnitRange) = UnitRange(A)
802828# These two helpers are deliberately not exported; their meaning can be very different in
803829# other scenarios and will be very likely to cause name conflicts if exported.
804830# ####
831+
832+ _halfroundInt (v, r:: RoundingMode ) = div (v, 2 , r)
833+
805834function center (A:: AbstractArray , r:: RoundingMode = RoundDown)
806835 map (axes (A)) do inds
807- round (Int, ( length (inds)- 1 ) / 2 , r) + first (inds)
836+ _halfroundInt ( length (inds)- 1 , r) + first (inds)
808837 end
809838end
810839
0 commit comments