Skip to content

Commit b92d2c8

Browse files
authored
similar with IdOffsetRanges may use the parent axes types (#213)
* similar with IdOffsetRange * Add test for similar(A, T, axes) * allow a mix of axis types * update comment * update similar(::Type, axes) * Add tests for mixed axis types * fix comment * rename functions * rename helper function
1 parent 8b4cc47 commit b92d2c8

File tree

3 files changed

+72
-8
lines changed

3 files changed

+72
-8
lines changed

src/OffsetArrays.jl

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -271,10 +271,34 @@ end
271271

272272
Base.similar(A::OffsetArray, ::Type{T}, dims::Dims) where T =
273273
similar(parent(A), T, dims)
274-
function Base.similar(A::AbstractArray, ::Type{T}, inds::Tuple{OffsetAxisKnownLength,Vararg{OffsetAxisKnownLength}}) where T
275-
B = similar(A, T, map(_indexlength, inds))
276-
return OffsetArray(B, map(_offset, axes(B), inds))
274+
function Base.similar(A::AbstractArray, ::Type{T}, shape::Tuple{OffsetAxisKnownLength,Vararg{OffsetAxisKnownLength}}) where T
275+
# strip IdOffsetRanges to extract the parent range and use it to generate the array
276+
new_shape = map(_strip_IdOffsetRange, shape)
277+
# route through _similar_axes_or_length to avoid a stack overflow if map(_strip_IdOffsetRange, shape) === shape
278+
# This tries to use new_shape directly in similar if similar(A, T, ::typeof(new_shape)) is defined
279+
# If this fails, it calls similar(A, T, map(_indexlength, new_shape)) to use the size along each axis
280+
# to generate the new array
281+
P = _similar_axes_or_length(A, T, new_shape, shape)
282+
return OffsetArray(P, map(_offset, axes(P), shape))
277283
end
284+
function Base.similar(::Type{T}, shape::Tuple{OffsetAxisKnownLength,Vararg{OffsetAxisKnownLength}}) where {T<:AbstractArray}
285+
new_shape = map(_strip_IdOffsetRange, shape)
286+
P = _similar_axes_or_length(T, new_shape, shape)
287+
OffsetArray(P, map(_offset, axes(P), shape))
288+
end
289+
# Try to use the axes to generate the parent array type
290+
# This is useful if the axes have special meanings, such as with static arrays
291+
# This method is hit if at least one axis provided to similar(A, T, axes) is an IdOffsetRange
292+
# For example this is hit when similar(A::OffsetArray) is called,
293+
# which expands to similar(A, eltype(A), axes(A))
294+
_similar_axes_or_length(A, T, ax, ::Any) = similar(A, T, ax)
295+
_similar_axes_or_length(AT, ax, ::Any) = similar(AT, ax)
296+
# Handle the general case by resorting to lengths along each axis
297+
# This is hit if none of the axes provided to similar(A, T, axes) are IdOffsetRanges,
298+
# and if similar(A, T, axes::AX) is not defined for the type AX.
299+
# In this case the best that we can do is to create a mutable array of the correct size
300+
_similar_axes_or_length(A, T, ax::I, ::I) where {I} = similar(A, T, map(_indexlength, ax))
301+
_similar_axes_or_length(AT, ax::I, ::I) where {I} = similar(AT, map(_indexlength, ax))
278302

279303
# reshape accepts a single colon
280304
Base.reshape(A::AbstractArray, inds::OffsetAxis...) = reshape(A, inds)
@@ -295,11 +319,6 @@ Base.reshape(A::OffsetVector, ::Colon) = A
295319
Base.reshape(A::OffsetArray, inds::Union{Int,Colon}...) = reshape(parent(A), inds)
296320
Base.reshape(A::OffsetArray, inds::Tuple{Vararg{Union{Int,Colon}}}) = reshape(parent(A), inds)
297321

298-
function Base.similar(::Type{T}, shape::Tuple{OffsetAxisKnownLength,Vararg{OffsetAxisKnownLength}}) where {T<:AbstractArray}
299-
P = T(undef, map(_indexlength, shape))
300-
OffsetArray(P, map(_offset, axes(P), shape))
301-
end
302-
303322
Base.fill(v, inds::NTuple{N, Union{Integer, AbstractUnitRange}}) where {N} =
304323
fill!(similar(Array{typeof(v)}, inds), v)
305324
Base.zeros(::Type{T}, inds::NTuple{N, Union{Integer, AbstractUnitRange}}) where {T, N} =

src/utils.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ _indexlength(r::AbstractRange) = length(r)
77
_indexlength(i::Integer) = i
88
_indexlength(i::Colon) = Colon()
99

10+
_strip_IdOffsetRange(r::IdOffsetRange) = parent(r)
11+
_strip_IdOffsetRange(r) = r
12+
1013
_offset(axparent::AbstractUnitRange, ax::AbstractUnitRange) = first(ax) - first(axparent)
1114
_offset(axparent::AbstractUnitRange, ax::Integer) = 1 - first(axparent)
1215

test/runtests.jl

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1397,6 +1397,48 @@ end
13971397
@test isa(B, OffsetArray{Int, 2})
13981398
@test axes(B) == (0:0, 1:3)
13991399

1400+
s = @SVector[i for i in 1:10]
1401+
so = OffsetArray(s, 4);
1402+
@test typeof(parent(similar(so))) == typeof(similar(s))
1403+
@test typeof(parent(similar(so, eltype(so), axes(so)))) == typeof(similar(s))
1404+
1405+
s = SArray{Tuple{2,2,2},Int,3,8}((1,2,3,4,5,6,7,8))
1406+
so = OffsetArray(s, 0, 0, 0)
1407+
A = similar(so)
1408+
@test A isa OffsetArray
1409+
@test parent(A) isa StaticArray
1410+
for ax in Any[(axes(s,1), axes(so)[2:3]...), (axes(so,1), axes(s)[2:3]...)]
1411+
A = similar(so, Int, ax)
1412+
@test A isa OffsetArray
1413+
@test parent(A) isa StaticArray
1414+
@test axes(A) == ax
1415+
end
1416+
1417+
# check with an unseen axis type
1418+
A = similar(ones(1), Int, ZeroBasedUnitRange(3:4), 4:5)
1419+
@test eltype(A) === Int
1420+
@test axes(A) == (3:4, 4:5)
1421+
A = similar(ones(1), Int, IdOffsetRange(ZeroBasedUnitRange(3:4), 2), 4:5)
1422+
@test eltype(A) === Int
1423+
@test axes(A) == (5:6, 4:5)
1424+
A = similar(ones(1), Int, IdOffsetRange(ZeroBasedUnitRange(3:4), 2), 4)
1425+
@test eltype(A) === Int
1426+
@test axes(A) == (5:6, 1:4)
1427+
1428+
# test for similar(::Type, ax)
1429+
indsoffset = (IdOffsetRange(SOneTo(2), 2),)
1430+
A = similar(Array{Int}, indsoffset)
1431+
@test parent(A) isa StaticArray
1432+
@test axes(A) == (3:4,)
1433+
A = similar(Array{Int}, (indsoffset..., SOneTo(3)))
1434+
@test parent(A) isa StaticArray
1435+
@test axes(A) == (3:4, 1:3)
1436+
1437+
s = SArray{Tuple{2,2},Int,2,4}((1,2,3,4));
1438+
so = OffsetArray(s, 2, 2);
1439+
so2 = so .+ 1;
1440+
@test parent(so2) isa StaticArray
1441+
14001442
@test_throws MethodError similar(A, (:,))
14011443
@test_throws MethodError similar(A, (: ,:))
14021444
@test_throws MethodError similar(A, (: ,2))

0 commit comments

Comments
 (0)