|
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 |
| -module OffsetArrays |
| 8 | +module OffsetArrays # OffsetArrays@1.0.0 without compat and docstrings |
9 | 9 |
|
10 |
| -using Base: Indices, IndexCartesian, IndexLinear, tail |
| 10 | +using Base: Indices, tail, @propagate_inbounds |
| 11 | +@static if !isdefined(Base, :IdentityUnitRange) |
| 12 | + const IdentityUnitRange = Base.Slice |
| 13 | +else |
| 14 | + using Base: IdentityUnitRange |
| 15 | +end |
| 16 | + |
| 17 | +export OffsetArray, OffsetVector |
| 18 | + |
| 19 | +struct IdOffsetRange{T<:Integer,I<:AbstractUnitRange{T}} <: AbstractUnitRange{T} |
| 20 | + parent::I |
| 21 | + offset::T |
| 22 | +end |
| 23 | +IdOffsetRange(r::AbstractUnitRange{T}, offset::Integer) where T = |
| 24 | + IdOffsetRange{T,typeof(r)}(r, convert(T, offset)) |
| 25 | + |
| 26 | +@inline Base.axes(r::IdOffsetRange) = (Base.axes1(r),) |
| 27 | +@inline Base.axes1(r::IdOffsetRange) = IdOffsetRange(Base.axes1(r.parent), r.offset) |
| 28 | +@inline Base.unsafe_indices(r::IdOffsetRange) = (r,) |
| 29 | +@inline Base.length(r::IdOffsetRange) = length(r.parent) |
| 30 | + |
| 31 | +function Base.iterate(r::IdOffsetRange) |
| 32 | + ret = iterate(r.parent) |
| 33 | + ret === nothing && return nothing |
| 34 | + return (ret[1] + r.offset, ret[2]) |
| 35 | +end |
| 36 | +function Base.iterate(r::IdOffsetRange, i) where T |
| 37 | + ret = iterate(r.parent, i) |
| 38 | + ret === nothing && return nothing |
| 39 | + return (ret[1] + r.offset, ret[2]) |
| 40 | +end |
| 41 | + |
| 42 | +@inline Base.first(r::IdOffsetRange) = first(r.parent) + r.offset |
| 43 | +@inline Base.last(r::IdOffsetRange) = last(r.parent) + r.offset |
| 44 | + |
| 45 | +@propagate_inbounds Base.getindex(r::IdOffsetRange, i::Integer) = r.parent[i - r.offset] + r.offset |
| 46 | +@propagate_inbounds function Base.getindex(r::IdOffsetRange, s::AbstractUnitRange{<:Integer}) |
| 47 | + return r.parent[s .- r.offset] .+ r.offset |
| 48 | +end |
| 49 | + |
| 50 | +Base.show(io::IO, r::IdOffsetRange) = print(io, first(r), ':', last(r)) |
11 | 51 |
|
12 |
| -export OffsetArray |
| 52 | +# Optimizations |
| 53 | +@inline Base.checkindex(::Type{Bool}, inds::IdOffsetRange, i::Real) = Base.checkindex(Bool, inds.parent, i - inds.offset) |
13 | 54 |
|
| 55 | +## OffsetArray |
14 | 56 | struct OffsetArray{T,N,AA<:AbstractArray} <: AbstractArray{T,N}
|
15 | 57 | parent::AA
|
16 | 58 | offsets::NTuple{N,Int}
|
17 | 59 | end
|
18 | 60 | OffsetVector{T,AA<:AbstractArray} = OffsetArray{T,1,AA}
|
19 | 61 |
|
20 |
| -OffsetArray(A::AbstractArray{T,N}, offsets::NTuple{N,Int}) where {T,N} = OffsetArray{T,N,typeof(A)}(A, offsets) |
21 |
| -OffsetArray(A::AbstractArray{T,N}, offsets::Vararg{Int,N}) where {T,N} = OffsetArray(A, offsets) |
| 62 | +## OffsetArray constructors |
22 | 63 |
|
23 |
| -OffsetArray{T,N}(::UndefInitializer, inds::Indices{N}) where {T,N} = |
24 |
| - OffsetArray{T,N,Array{T,N}}(Array{T,N}(undef, map(length, inds)), map(indsoffset, inds)) |
25 |
| -OffsetArray{T}(::UndefInitializer, inds::Indices{N}) where {T,N} = |
26 |
| - OffsetArray{T,N}(undef, inds) |
| 64 | +offset(axparent::AbstractUnitRange, ax::AbstractUnitRange) = first(ax) - first(axparent) |
| 65 | +offset(axparent::AbstractUnitRange, ax::Integer) = 1 - first(axparent) |
27 | 66 |
|
28 |
| -Base.IndexStyle(::Type{T}) where {T<:OffsetArray} = Base.IndexStyle(parenttype(T)) |
| 67 | +function OffsetArray(A::AbstractArray{T,N}, offsets::NTuple{N,Int}) where {T,N} |
| 68 | + OffsetArray{T,N,typeof(A)}(A, offsets) |
| 69 | +end |
| 70 | +OffsetArray(A::AbstractArray{T,0}, offsets::Tuple{}) where T = |
| 71 | + OffsetArray{T,0,typeof(A)}(A, ()) |
| 72 | + |
| 73 | +OffsetArray(A::AbstractArray{T,N}, offsets::Vararg{Int,N}) where {T,N} = |
| 74 | + OffsetArray(A, offsets) |
| 75 | +OffsetArray(A::AbstractArray{T,0}) where {T} = OffsetArray(A, ()) |
| 76 | + |
| 77 | +const ArrayInitializer = Union{UndefInitializer, Missing, Nothing} |
| 78 | +OffsetArray{T,N}(init::ArrayInitializer, inds::Indices{N}) where {T,N} = |
| 79 | + OffsetArray(Array{T,N}(init, map(indexlength, inds)), map(indexoffset, inds)) |
| 80 | +OffsetArray{T}(init::ArrayInitializer, inds::Indices{N}) where {T,N} = OffsetArray{T,N}(init, inds) |
| 81 | +OffsetArray{T,N}(init::ArrayInitializer, inds::Vararg{AbstractUnitRange,N}) where {T,N} = OffsetArray{T,N}(init, inds) |
| 82 | +OffsetArray{T}(init::ArrayInitializer, inds::Vararg{AbstractUnitRange,N}) where {T,N} = OffsetArray{T,N}(init, inds) |
| 83 | + |
| 84 | +# OffsetVector constructors |
| 85 | +OffsetVector(A::AbstractVector, offset) = OffsetArray(A, offset) |
| 86 | +OffsetVector{T}(init::ArrayInitializer, inds::AbstractUnitRange) where {T} = OffsetArray{T}(init, inds) |
| 87 | + |
| 88 | +function OffsetArray(A::AbstractArray{T,N}, inds::NTuple{N,AbstractUnitRange}) where {T,N} |
| 89 | + axparent = axes(A) |
| 90 | + lA = map(length, axparent) |
| 91 | + lI = map(length, inds) |
| 92 | + lA == lI || throw(DimensionMismatch("supplied axes do not agree with the size of the array (got size $lA for the array and $lI for the indices")) |
| 93 | + OffsetArray(A, map(offset, axparent, inds)) |
| 94 | +end |
| 95 | +OffsetArray(A::AbstractArray{T,N}, inds::Vararg{AbstractUnitRange,N}) where {T,N} = |
| 96 | + OffsetArray(A, inds) |
| 97 | + |
| 98 | +# avoid a level of indirection when nesting OffsetArrays |
| 99 | +function OffsetArray(A::OffsetArray, inds::NTuple{N,AbstractUnitRange}) where {N} |
| 100 | + OffsetArray(parent(A), inds) |
| 101 | +end |
| 102 | +OffsetArray(A::OffsetArray{T,0}, inds::Tuple{}) where {T} = OffsetArray(parent(A), ()) |
| 103 | +# OffsetArray(A::OffsetArray{T,N}, inds::Tuple{}) where {T,N} = error("this should never be called") |
| 104 | + |
| 105 | +Base.IndexStyle(::Type{OA}) where {OA<:OffsetArray} = IndexStyle(parenttype(OA)) |
29 | 106 | parenttype(::Type{OffsetArray{T,N,AA}}) where {T,N,AA} = AA
|
30 | 107 | parenttype(A::OffsetArray) = parenttype(typeof(A))
|
31 | 108 |
|
32 | 109 | Base.parent(A::OffsetArray) = A.parent
|
33 | 110 |
|
34 |
| -Base.size(A::OffsetArray) = size(A.parent) |
35 |
| -Base.size(A::OffsetArray, d) = size(A.parent, d) |
36 | 111 | Base.eachindex(::IndexCartesian, A::OffsetArray) = CartesianIndices(axes(A))
|
37 |
| -Base.eachindex(::IndexLinear, A::OffsetVector) = axes(A, 1) |
| 112 | +Base.eachindex(::IndexLinear, A::OffsetVector) = axes(A, 1) |
38 | 113 |
|
39 |
| -# Implementations of indices and axes1. Since bounds-checking is |
40 |
| -# performance-critical and relies on indices, these are usually worth |
41 |
| -# optimizing thoroughly. |
42 |
| -@inline Base.axes(A::OffsetArray, d) = 1 <= d <= length(A.offsets) ? Base.IdentityUnitRange(axes(parent(A))[d] .+ A.offsets[d]) : Base.IdentityUnitRange(1:1) |
43 |
| -@inline Base.axes(A::OffsetArray) = _indices(axes(parent(A)), A.offsets) # would rather use ntuple, but see #15276 |
44 |
| -@inline _indices(inds, offsets) = (Base.IdentityUnitRange(inds[1] .+ offsets[1]), _indices(tail(inds), tail(offsets))...) |
45 |
| -_indices(::Tuple{}, ::Tuple{}) = () |
46 |
| -Base.axes1(A::OffsetArray{T,0}) where {T} = Base.IdentityUnitRange(1:1) # we only need to specialize this one |
| 114 | +@inline Base.size(A::OffsetArray) = size(parent(A)) |
| 115 | +@inline Base.size(A::OffsetArray, d) = size(parent(A), d) |
47 | 116 |
|
48 |
| -const OffsetAxis = Union{Integer, UnitRange, Base.IdentityUnitRange{<:UnitRange}, Base.OneTo} |
49 |
| -function Base.similar(A::OffsetArray, T::Type, dims::Dims) |
50 |
| - B = similar(parent(A), T, dims) |
| 117 | +@inline Base.axes(A::OffsetArray) = map(IdOffsetRange, axes(parent(A)), A.offsets) |
| 118 | +@inline Base.axes(A::OffsetArray, d) = d <= ndims(A) ? IdOffsetRange(axes(parent(A), d), A.offsets[d]) : Base.OneTo(1) |
| 119 | +@inline Base.axes1(A::OffsetArray{T,0}) where {T} = Base.OneTo(1) # we only need to specialize this one |
| 120 | + |
| 121 | +const OffsetAxis = Union{Integer, UnitRange, Base.OneTo, IdentityUnitRange, IdOffsetRange, Colon} |
| 122 | +Base.similar(A::OffsetArray, ::Type{T}, dims::Dims) where T = |
| 123 | + similar(parent(A), T, dims) |
| 124 | +function Base.similar(A::AbstractArray, ::Type{T}, inds::Tuple{OffsetAxis,Vararg{OffsetAxis}}) where T |
| 125 | + B = similar(A, T, map(indexlength, inds)) |
| 126 | + return OffsetArray(B, map(offset, axes(B), inds)) |
51 | 127 | end
|
52 |
| -function Base.similar(A::AbstractArray, T::Type, inds::Tuple{OffsetAxis,Vararg{OffsetAxis}}) |
53 |
| - B = similar(A, T, map(indslength, inds)) |
54 |
| - OffsetArray(B, map(indsoffset, inds)) |
| 128 | + |
| 129 | +Base.reshape(A::AbstractArray, inds::OffsetAxis...) = reshape(A, inds) |
| 130 | +function Base.reshape(A::AbstractArray, inds::Tuple{OffsetAxis,Vararg{OffsetAxis}}) |
| 131 | + AR = reshape(A, map(indexlength, inds)) |
| 132 | + return OffsetArray(AR, map(offset, axes(AR), inds)) |
55 | 133 | end
|
56 | 134 |
|
57 |
| -Base.similar(::Type{T}, shape::Tuple{OffsetAxis,Vararg{OffsetAxis}}) where {T<:AbstractArray} = |
58 |
| - OffsetArray(T(undef, map(indslength, shape)), map(indsoffset, shape)) |
| 135 | +# Reshaping OffsetArrays can "pop" the original OffsetArray wrapper and return |
| 136 | +# an OffsetArray(reshape(...)) instead of an OffsetArray(reshape(OffsetArray(...))) |
| 137 | +Base.reshape(A::OffsetArray, inds::Tuple{OffsetAxis,Vararg{OffsetAxis}}) = |
| 138 | + OffsetArray(reshape(parent(A), map(indexlength, inds)), map(indexoffset, inds)) |
| 139 | +# And for non-offset axes, we can just return a reshape of the parent directly |
| 140 | +Base.reshape(A::OffsetArray, inds::Tuple{Union{Integer,Base.OneTo},Vararg{Union{Integer,Base.OneTo}}}) = reshape(parent(A), inds) |
| 141 | +Base.reshape(A::OffsetArray, inds::Dims) = reshape(parent(A), inds) |
| 142 | +Base.reshape(A::OffsetArray, ::Colon) = A |
| 143 | +Base.reshape(A::OffsetArray, inds::Union{Int,Colon}...) = reshape(parent(A), inds) |
| 144 | +Base.reshape(A::OffsetArray, inds::Tuple{Vararg{Union{Int,Colon}}}) = reshape(parent(A), inds) |
59 | 145 |
|
60 |
| -Base.reshape(A::AbstractArray, inds::Tuple{OffsetAxis,Vararg{OffsetAxis}}) = OffsetArray(reshape(A, map(indslength, inds)), map(indsoffset, inds)) |
| 146 | +function Base.similar(::Type{T}, shape::Tuple{OffsetAxis,Vararg{OffsetAxis}}) where {T<:AbstractArray} |
| 147 | + P = T(undef, map(indexlength, shape)) |
| 148 | + OffsetArray(P, map(offset, axes(P), shape)) |
| 149 | +end |
61 | 150 |
|
62 | 151 | Base.fill(v, inds::NTuple{N, Union{Integer, AbstractUnitRange}}) where {N} =
|
63 |
| - fill!(OffsetArray(Array{typeof(v), N}(undef, map(indslength, inds)), map(indsoffset, inds)), v) |
| 152 | + fill!(OffsetArray(Array{typeof(v), N}(undef, map(indexlength, inds)), map(indexoffset, inds)), v) |
64 | 153 | Base.zeros(::Type{T}, inds::NTuple{N, Union{Integer, AbstractUnitRange}}) where {T, N} =
|
65 |
| - fill!(OffsetArray(Array{T, N}(undef, map(indslength, inds)), map(indsoffset, inds)), zero(T)) |
| 154 | + fill!(OffsetArray(Array{T, N}(undef, map(indexlength, inds)), map(indexoffset, inds)), zero(T)) |
66 | 155 | Base.ones(::Type{T}, inds::NTuple{N, Union{Integer, AbstractUnitRange}}) where {T, N} =
|
67 |
| - fill!(OffsetArray(Array{T, N}(undef, map(indslength, inds)), map(indsoffset, inds)), one(T)) |
| 156 | + fill!(OffsetArray(Array{T, N}(undef, map(indexlength, inds)), map(indexoffset, inds)), one(T)) |
68 | 157 | Base.trues(inds::NTuple{N, Union{Integer, AbstractUnitRange}}) where {N} =
|
69 |
| - fill!(OffsetArray(BitArray{N}(undef, map(indslength, inds)), map(indsoffset, inds)), true) |
| 158 | + fill!(OffsetArray(BitArray{N}(undef, map(indexlength, inds)), map(indexoffset, inds)), true) |
70 | 159 | Base.falses(inds::NTuple{N, Union{Integer, AbstractUnitRange}}) where {N} =
|
71 |
| - fill!(OffsetArray(BitArray{N}(undef, map(indslength, inds)), map(indsoffset, inds)), false) |
72 |
| - |
73 |
| -@inline function Base.getindex(A::OffsetArray{T,N}, I::Vararg{Int,N}) where {T,N} |
74 |
| - checkbounds(A, I...) |
75 |
| - @inbounds ret = parent(A)[offset(A.offsets, I)...] |
76 |
| - ret |
77 |
| -end |
78 |
| -# Vectors don't support one-based linear indexing; they always use the offsets |
79 |
| -@inline function Base.getindex(A::OffsetVector, i::Int) |
80 |
| - checkbounds(A, i) |
81 |
| - @inbounds ret = parent(A)[offset(A.offsets, (i,))[1]] |
82 |
| - ret |
83 |
| -end |
84 |
| -# But multidimensional arrays allow one-based linear indexing |
85 |
| -@inline function Base.getindex(A::OffsetArray, i::Int) |
86 |
| - checkbounds(A, i) |
87 |
| - @inbounds ret = parent(A)[i] |
88 |
| - ret |
89 |
| -end |
90 |
| -@inline function Base.setindex!(A::OffsetArray{T,N}, val, I::Vararg{Int,N}) where {T,N} |
91 |
| - checkbounds(A, I...) |
92 |
| - @inbounds parent(A)[offset(A.offsets, I)...] = val |
| 160 | + fill!(OffsetArray(BitArray{N}(undef, map(indexlength, inds)), map(indexoffset, inds)), false) |
| 161 | + |
| 162 | +## Indexing |
| 163 | + |
| 164 | +# Note this gets the index of the parent *array*, not the index of the parent *range* |
| 165 | +# Here's how one can think about this: |
| 166 | +# Δi = i - first(r) |
| 167 | +# i′ = first(r.parent) + Δi |
| 168 | +# and one obtains the result below. |
| 169 | +parentindex(r::IdOffsetRange, i) = i - r.offset |
| 170 | + |
| 171 | +@propagate_inbounds function Base.getindex(A::OffsetArray{T,N}, I::Vararg{Int,N}) where {T,N} |
| 172 | + J = map(parentindex, axes(A), I) |
| 173 | + return parent(A)[J...] |
| 174 | +end |
| 175 | + |
| 176 | +@propagate_inbounds Base.getindex(A::OffsetVector, i::Int) = parent(A)[parentindex(Base.axes1(A), i)] |
| 177 | +@propagate_inbounds Base.getindex(A::OffsetArray, i::Int) = parent(A)[i] |
| 178 | + |
| 179 | +@propagate_inbounds function Base.setindex!(A::OffsetArray{T,N}, val, I::Vararg{Int,N}) where {T,N} |
| 180 | + @boundscheck checkbounds(A, I...) |
| 181 | + J = @inbounds map(parentindex, axes(A), I) |
| 182 | + @inbounds parent(A)[J...] = val |
93 | 183 | val
|
94 | 184 | end
|
95 |
| -@inline function Base.setindex!(A::OffsetVector, val, i::Int) |
96 |
| - checkbounds(A, i) |
97 |
| - @inbounds parent(A)[offset(A.offsets, (i,))[1]] = val |
| 185 | + |
| 186 | +@propagate_inbounds function Base.setindex!(A::OffsetVector, val, i::Int) |
| 187 | + @boundscheck checkbounds(A, i) |
| 188 | + @inbounds parent(A)[parentindex(Base.axes1(A), i)] = val |
98 | 189 | val
|
99 | 190 | end
|
100 |
| -@inline function Base.setindex!(A::OffsetArray, val, i::Int) |
101 |
| - checkbounds(A, i) |
| 191 | +@propagate_inbounds function Base.setindex!(A::OffsetArray, val, i::Int) |
| 192 | + @boundscheck checkbounds(A, i) |
102 | 193 | @inbounds parent(A)[i] = val
|
103 | 194 | val
|
104 | 195 | end
|
105 | 196 |
|
106 |
| -@inline function Base.deleteat!(A::OffsetArray, i::Int) |
107 |
| - checkbounds(A, i) |
108 |
| - @inbounds deleteat!(parent(A), offset(A.offsets, (i,))[1]) |
109 |
| -end |
| 197 | +# For fast broadcasting: ref https://discourse.julialang.org/t/why-is-there-a-performance-hit-on-broadcasting-with-offsetarrays/32194 |
| 198 | +Base.dataids(A::OffsetArray) = Base.dataids(parent(A)) |
| 199 | +Broadcast.broadcast_unalias(dest::OffsetArray, src::OffsetArray) = parent(dest) === parent(src) ? src : Broadcast.unalias(dest, src) |
110 | 200 |
|
111 |
| -@inline function Base.deleteat!(A::OffsetArray{T,N}, I::Vararg{Int, N}) where {T,N} |
112 |
| - checkbounds(A, I...) |
113 |
| - @inbounds deleteat!(parent(A), offset(A.offsets, I)...) |
114 |
| -end |
| 201 | +### Special handling for AbstractRange |
115 | 202 |
|
116 |
| -@inline function Base.deleteat!(A::OffsetArray, i::UnitRange{Int}) |
117 |
| - checkbounds(A, first(i)) |
118 |
| - checkbounds(A, last(i)) |
119 |
| - first_idx = offset(A.offsets, (first(i),))[1] |
120 |
| - last_idx = offset(A.offsets, (last(i),))[1] |
121 |
| - @inbounds deleteat!(parent(A), first_idx:last_idx) |
122 |
| -end |
| 203 | +const OffsetRange{T} = OffsetArray{T,1,<:AbstractRange{T}} |
| 204 | +const IIUR = IdentityUnitRange{S} where S<:AbstractUnitRange{T} where T<:Integer |
| 205 | + |
| 206 | +Base.step(a::OffsetRange) = step(parent(a)) |
| 207 | + |
| 208 | +Base.getindex(a::OffsetRange, r::OffsetRange) = OffsetArray(a[parent(r)], r.offsets) |
| 209 | +Base.getindex(a::OffsetRange, r::AbstractRange) = a.parent[r .- a.offsets[1]] |
| 210 | +Base.getindex(a::AbstractRange, r::OffsetRange) = OffsetArray(a[parent(r)], r.offsets) |
123 | 211 |
|
124 |
| -function Base.push!(a::OffsetArray{T,1}, item) where T |
125 |
| - # convert first so we don't grow the array if the assignment won't work |
126 |
| - itemT = convert(T, item) |
127 |
| - resize!(a, length(a)+1) |
128 |
| - a[end] = itemT |
129 |
| - return a |
| 212 | +@propagate_inbounds Base.getindex(r::UnitRange, s::IIUR) = |
| 213 | + OffsetArray(r[s.indices], s) |
| 214 | + |
| 215 | +@propagate_inbounds Base.getindex(r::StepRange, s::IIUR) = |
| 216 | + OffsetArray(r[s.indices], s) |
| 217 | + |
| 218 | +@inline @propagate_inbounds Base.getindex(r::StepRangeLen{T,<:Base.TwicePrecision,<:Base.TwicePrecision}, s::IIUR) where T = |
| 219 | + OffsetArray(r[s.indices], s) |
| 220 | +@inline @propagate_inbounds Base.getindex(r::StepRangeLen{T}, s::IIUR) where {T} = |
| 221 | + OffsetArray(r[s.indices], s) |
| 222 | + |
| 223 | +@inline @propagate_inbounds Base.getindex(r::LinRange, s::IIUR) = |
| 224 | + OffsetArray(r[s.indices], s) |
| 225 | + |
| 226 | +function Base.show(io::IO, r::OffsetRange) |
| 227 | + show(io, r.parent) |
| 228 | + o = r.offsets[1] |
| 229 | + print(io, " with indices ", o+1:o+length(r)) |
130 | 230 | end
|
| 231 | +Base.show(io::IO, ::MIME"text/plain", r::OffsetRange) = show(io, r) |
| 232 | + |
| 233 | +### Convenience functions ### |
131 | 234 |
|
132 |
| -# Computing a shifted index (subtracting the offset) |
133 |
| -offset(offsets::NTuple{N,Int}, inds::NTuple{N,Int}) where {N} = _offset((), offsets, inds) |
134 |
| -_offset(out, ::Tuple{}, ::Tuple{}) = out |
135 |
| -@inline _offset(out, offsets, inds) = _offset((out..., inds[1]-offsets[1]), Base.tail(offsets), Base.tail(inds)) |
| 235 | +Base.fill(x, inds::Tuple{UnitRange,Vararg{UnitRange}}) = |
| 236 | + fill!(OffsetArray{typeof(x)}(undef, inds), x) |
| 237 | +@inline Base.fill(x, ind1::UnitRange, inds::UnitRange...) = fill(x, (ind1, inds...)) |
136 | 238 |
|
137 |
| -indsoffset(r::AbstractRange) = first(r) - 1 |
138 |
| -indsoffset(i::Integer) = 0 |
139 |
| -indslength(r::AbstractRange) = length(r) |
140 |
| -indslength(i::Integer) = i |
141 | 239 |
|
| 240 | +### Some mutating functions defined only for OffsetVector ### |
142 | 241 |
|
143 | 242 | Base.resize!(A::OffsetVector, nl::Integer) = (resize!(A.parent, nl); A)
|
| 243 | +Base.push!(A::OffsetVector, x...) = (push!(A.parent, x...); A) |
| 244 | +Base.pop!(A::OffsetVector) = pop!(A.parent) |
| 245 | +Base.empty!(A::OffsetVector) = (empty!(A.parent); A) |
| 246 | + |
| 247 | +### Low-level utilities ### |
144 | 248 |
|
| 249 | +indexoffset(r::AbstractRange) = first(r) - 1 |
| 250 | +indexoffset(i::Integer) = 0 |
| 251 | +indexoffset(i::Colon) = 0 |
| 252 | +indexlength(r::AbstractRange) = length(r) |
| 253 | +indexlength(i::Integer) = i |
| 254 | +indexlength(i::Colon) = Colon() |
| 255 | + |
| 256 | +function Base.showarg(io::IO, a::OffsetArray, toplevel) |
| 257 | + print(io, "OffsetArray(") |
| 258 | + Base.showarg(io, parent(a), false) |
| 259 | + if ndims(a) > 0 |
| 260 | + print(io, ", ") |
| 261 | + printindices(io, axes(a)...) |
| 262 | + end |
| 263 | + print(io, ')') |
| 264 | + toplevel && print(io, " with eltype ", eltype(a)) |
| 265 | +end |
| 266 | +printindices(io::IO, ind1, inds...) = |
| 267 | + (print(io, _unslice(ind1), ", "); printindices(io, inds...)) |
| 268 | +printindices(io::IO, ind1) = print(io, _unslice(ind1)) |
| 269 | +_unslice(x) = x |
| 270 | +_unslice(x::IdentityUnitRange) = x.indices |
| 271 | + |
| 272 | +function no_offset_view(A::AbstractArray) |
| 273 | + if Base.has_offset_axes(A) |
| 274 | + OffsetArray(A, map(r->1-first(r), axes(A))) |
| 275 | + else |
| 276 | + A |
| 277 | + end |
145 | 278 | end
|
| 279 | + |
| 280 | +no_offset_view(A::OffsetArray) = no_offset_view(parent(A)) |
| 281 | + |
| 282 | +end # module |
0 commit comments