Skip to content

Commit 39427dd

Browse files
authored
map preserves ranges (#232)
* map preserves ranges * simplify map * rename maybewrapoffset to indexedby * add tests for Base.OneTo * split methods for IdentityUnitRange and IdOffsetRange * future-proof IIUR
1 parent 7291c1f commit 39427dd

File tree

4 files changed

+47
-18
lines changed

4 files changed

+47
-18
lines changed

src/OffsetArrays.jl

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -451,11 +451,11 @@ Base.checkindex(::Type{Bool}, inds::AbstractUnitRange, or::OffsetRange) = Base.c
451451

452452
# If both the arguments are offset, we may unwrap the indices to call (::OffsetArray)[::AbstractRange{Int}]
453453
@propagate_inbounds function Base.getindex(A::OffsetArray, r::OffsetRange{Int})
454-
_maybewrapoffset(A[parent(r)], axes(r))
454+
_indexedby(A[parent(r)], axes(r))
455455
end
456456
# If the indices are offset, we may unwrap them and pass the parent to getindex
457457
@propagate_inbounds function Base.getindex(A::AbstractRange, r::OffsetRange{Int})
458-
_maybewrapoffset(A[parent(r)], axes(r))
458+
_indexedby(A[parent(r)], axes(r))
459459
end
460460

461461
# An OffsetUnitRange might use the rapid getindex(::Array, ::AbstractUnitRange{Int}) for contiguous indexing
@@ -469,7 +469,7 @@ end
469469
if VERSION <= v"1.7.0-DEV.1039"
470470
@propagate_inbounds function Base.getindex(A::Array, r::Union{IdOffsetRange, IIUR})
471471
B = A[_contiguousindexingtype(r)]
472-
_maybewrapoffset(B, axes(r))
472+
_indexedby(B, axes(r))
473473
end
474474
end
475475

@@ -478,20 +478,20 @@ end
478478
@boundscheck checkbounds(A, r)
479479
# nD OffsetArrays do not have their linear indices shifted, so we may forward the indices provided to the parent
480480
@inbounds B = parent(A)[_contiguousindexingtype(r)]
481-
_maybewrapoffset(B, axes(r))
481+
_indexedby(B, axes(r))
482482
end
483483
@inline function Base.getindex(A::OffsetVector, r::AbstractUnitRange{Int})
484484
@boundscheck checkbounds(A, r)
485485
# OffsetVectors may have their linear indices shifted, so we subtract the offset from the indices provided
486486
@inbounds B = parent(A)[_subtractoffset(r, A.offsets[1])]
487-
_maybewrapoffset(B, axes(r))
487+
_indexedby(B, axes(r))
488488
end
489489

490490
# This method added mainly to index an OffsetRange with another range
491491
@inline function Base.getindex(A::OffsetVector, r::AbstractRange{Int})
492492
@boundscheck checkbounds(A, r)
493493
@inbounds B = parent(A)[_subtractoffset(r, A.offsets[1])]
494-
_maybewrapoffset(B, axes(r))
494+
_indexedby(B, axes(r))
495495
end
496496

497497
# In general we would pass through getindex(A, I...) which calls to_indices(A, I) and finally to_index(I)
@@ -508,21 +508,26 @@ for OR in [:IIUR, :IdOffsetRange]
508508
@eval @inline function Base.getindex(r::$R, s::$OR)
509509
@boundscheck checkbounds(r, s)
510510
@inbounds pr = r[UnitRange(s)]
511-
_maybewrapoffset(pr, axes(s,1))
511+
_indexedby(pr, axes(s))
512512
end
513513
end
514514

515515
# this method is needed for ambiguity resolution
516516
@eval @inline function Base.getindex(r::StepRangeLen{T,<:Base.TwicePrecision,<:Base.TwicePrecision}, s::$OR) where T
517517
@boundscheck checkbounds(r, s)
518518
@inbounds pr = r[UnitRange(s)]
519-
_maybewrapoffset(pr, axes(s,1))
519+
_indexedby(pr, axes(s))
520520
end
521521
end
522522

523523
# eltype conversion
524524
# This may use specialized map methods for the parent
525525
Base.map(::Type{T}, O::OffsetArray) where {T} = parent_call(x -> map(T, x), O)
526+
Base.map(::Type{T}, r::IdOffsetRange) where {T<:Real} = _indexedby(map(T, UnitRange(r)), axes(r))
527+
if eltype(IIUR) === Int
528+
# This is type-piracy, but there is no way to convert an IdentityUnitRange to a non-Int type in Base
529+
Base.map(::Type{T}, r::IdentityUnitRange) where {T<:Real} = _indexedby(map(T, UnitRange(r)), axes(r))
530+
end
526531

527532
# mapreduce is faster with an IdOffsetRange than with an OffsetUnitRange
528533
# We therefore convert OffsetUnitRanges to IdOffsetRanges with the same values and axes

src/axes.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ end
179179
@inline function Base.getindex(r::IdOffsetRange, s::AbstractUnitRange{<:Integer})
180180
@boundscheck checkbounds(r, s)
181181
@inbounds pr = r.parent[_subtractoffset(s, r.offset)] .+ r.offset
182-
_maybewrapoffset(pr, axes(s,1))
182+
_indexedby(pr, axes(s))
183183
end
184184
# The following method is required to avoid falling back to getindex(::AbstractUnitRange, ::StepRange{<:Integer})
185185
@inline function Base.getindex(r::IdOffsetRange, s::StepRange{<:Integer})

src/utils.jl

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,14 +77,16 @@ function _checkindices(N::Integer, indices, label)
7777
N == length(indices) || throw_argumenterror(N, indices, label)
7878
end
7979

80-
@inline _maybewrapoffset(r::AbstractVector, ax::Tuple{Any}) = _maybewrapoffset(r, ax[1])
81-
@inline _maybewrapoffset(r::AbstractUnitRange{<:Integer}, ::Base.OneTo) = no_offset_view(r)
82-
@inline _maybewrapoffset(r::AbstractVector, ::Base.OneTo) = no_offset_view(r)
83-
@inline function _maybewrapoffset(r::AbstractUnitRange{<:Integer}, ax::AbstractUnitRange)
84-
of = first(ax) - 1
80+
@inline _indexedby(r::AbstractVector, ax::Tuple{Any}) = _indexedby(r, ax[1])
81+
@inline _indexedby(r::AbstractUnitRange{<:Integer}, ::Base.OneTo) = no_offset_view(r)
82+
@inline _indexedby(r::AbstractUnitRange{Bool}, ::Base.OneTo) = no_offset_view(r)
83+
@inline _indexedby(r::AbstractVector, ::Base.OneTo) = no_offset_view(r)
84+
@inline function _indexedby(r::AbstractUnitRange{<:Integer}, ax::AbstractUnitRange)
85+
of = convert(eltype(r), first(ax) - 1)
8586
IdOffsetRange(_subtractoffset(r, of), of)
8687
end
87-
@inline _maybewrapoffset(r::AbstractVector, ax::AbstractUnitRange) = OffsetArray(r, ax)
88+
@inline _indexedby(r::AbstractUnitRange{Bool}, ax::AbstractUnitRange) = OffsetArray(r, ax)
89+
@inline _indexedby(r::AbstractVector, ax::AbstractUnitRange) = OffsetArray(r, ax)
8890

8991
# These functions are equivalent to the broadcasted operation r .- of
9092
# However these ensure that the result is an AbstractRange even if a specific

test/runtests.jl

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,18 +63,18 @@ for Z in [:ZeroBasedRange, :ZeroBasedUnitRange]
6363
for R in [:AbstractRange, :AbstractUnitRange, :StepRange]
6464
@eval @inline function Base.getindex(A::$Z, r::$R{<:Integer})
6565
@boundscheck checkbounds(A, r)
66-
OffsetArrays._maybewrapoffset(A.a[r .+ 1], axes(r))
66+
OffsetArrays._indexedby(A.a[r .+ 1], axes(r))
6767
end
6868
end
6969
for R in [:UnitRange, :StepRange, :StepRangeLen, :LinRange]
7070
@eval @inline function Base.getindex(A::$R, r::$Z)
7171
@boundscheck checkbounds(A, r)
72-
OffsetArrays._maybewrapoffset(A[r.a], axes(r))
72+
OffsetArrays._indexedby(A[r.a], axes(r))
7373
end
7474
end
7575
@eval @inline function Base.getindex(A::StepRangeLen{<:Any,<:Base.TwicePrecision,<:Base.TwicePrecision}, r::$Z)
7676
@boundscheck checkbounds(A, r)
77-
OffsetArrays._maybewrapoffset(A[r.a], axes(r))
77+
OffsetArrays._indexedby(A[r.a], axes(r))
7878
end
7979
end
8080

@@ -1759,6 +1759,28 @@ end
17591759
@test eltype(b) == BigInt
17601760
@test b == a
17611761
@test b isa OffsetArrays.OffsetRange
1762+
1763+
for ri in Any[2:3, Base.OneTo(2)]
1764+
for r in [IdentityUnitRange(ri), IdOffsetRange(ri), IdOffsetRange(ri, 1)]
1765+
for T in [Int8, Int16, Int32, Int64, Int128, BigInt, Float32, Float64, BigFloat]
1766+
r2 = map(T, r)
1767+
@test eltype(r2) == T
1768+
@test axes(r2) == axes(r)
1769+
@test all(((x,y),) -> isequal(x,y), zip(r, r2))
1770+
end
1771+
end
1772+
end
1773+
1774+
@testset "Bool" begin
1775+
for ri in Any[0:0, 0:1, 1:0, 1:1, Base.OneTo(0), Base.OneTo(1)]
1776+
for r = Any[IdentityUnitRange(ri), IdOffsetRange(ri), IdOffsetRange(ri .- 1, 1)]
1777+
r2 = map(Bool, r)
1778+
@test eltype(r2) == Bool
1779+
@test axes(r2) == axes(r)
1780+
@test all(((x,y),) -> isequal(x,y), zip(r, r2))
1781+
end
1782+
end
1783+
end
17621784
end
17631785
end
17641786

0 commit comments

Comments
 (0)