Skip to content

Commit ac9821e

Browse files
authored
Make IdOffsetRange printing round-trippable (#208)
1 parent fe95c00 commit ac9821e

File tree

2 files changed

+35
-13
lines changed

2 files changed

+35
-13
lines changed

src/axes.jl

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ i.e., it's the "identity," which is the origin of the "Id" in `IdOffsetRange`.
1111
The most common case is shifting a range that starts at 1 (either `1:n` or `Base.OneTo(n)`):
1212
```jldoctest; setup=:(import OffsetArrays)
1313
julia> ro = OffsetArrays.IdOffsetRange(1:3, -2)
14-
OffsetArrays.IdOffsetRange(-1:1)
14+
OffsetArrays.IdOffsetRange(values=-1:1, indices=-1:1)
1515
1616
julia> axes(ro, 1)
17-
OffsetArrays.IdOffsetRange(-1:1)
17+
OffsetArrays.IdOffsetRange(values=-1:1, indices=-1:1)
1818
1919
julia> ro[-1]
2020
-1
@@ -26,10 +26,10 @@ ERROR: BoundsError: attempt to access 3-element UnitRange{$Int} at index [5]
2626
If the range doesn't start at 1, the values may be different from the indices:
2727
```jldoctest; setup=:(import OffsetArrays)
2828
julia> ro = OffsetArrays.IdOffsetRange(11:13, -2)
29-
OffsetArrays.IdOffsetRange(9:11)
29+
OffsetArrays.IdOffsetRange(values=9:11, indices=-1:1)
3030
3131
julia> axes(ro, 1) # 11:13 is indexed by 1:3, and the offset is also applied to the axes
32-
OffsetArrays.IdOffsetRange(-1:1)
32+
OffsetArrays.IdOffsetRange(values=-1:1, indices=-1:1)
3333
3434
julia> ro[-1]
3535
9
@@ -41,7 +41,7 @@ ERROR: BoundsError: attempt to access 3-element UnitRange{$Int} at index [5]
4141
# Extended help
4242
4343
Construction/coercion preserves the (shifted) values of the input range, but may modify
44-
the indexes if required by the specified types. For example,
44+
the indices if required by the specified types. For example,
4545
4646
r = OffsetArrays.IdOffsetRange{Int,UnitRange{Int}}(3:4)
4747
@@ -78,10 +78,10 @@ struct IdOffsetRange{T<:Integer,I<:AbstractUnitRange{T}} <: AbstractUnitRange{T}
7878
offset::T
7979

8080
IdOffsetRange{T,I}(r::I, offset::T) where {T<:Integer,I<:AbstractUnitRange{T}} = new{T,I}(r, offset)
81-
82-
#= This method is necessary to avoid a StackOverflowError in IdOffsetRange{T,I}(r::IdOffsetRange, offset::Integer).
83-
The type signature in that method is more specific than IdOffsetRange{T,I}(r::I, offset::T),
84-
so it ends up calling itself if I <: IdOffsetRange.
81+
82+
#= This method is necessary to avoid a StackOverflowError in IdOffsetRange{T,I}(r::IdOffsetRange, offset::Integer).
83+
The type signature in that method is more specific than IdOffsetRange{T,I}(r::I, offset::T),
84+
so it ends up calling itself if I <: IdOffsetRange.
8585
=#
8686
function IdOffsetRange{T,IdOffsetRange{T,I}}(r::IdOffsetRange{T,I}, offset::T) where {T<:Integer,I<:AbstractUnitRange{T}}
8787
new{T,IdOffsetRange{T,I}}(r, offset)
@@ -111,8 +111,15 @@ function IdOffsetRange{T}(r::IdOffsetRange, offset::Integer = 0) where T<:Intege
111111
end
112112
IdOffsetRange(r::IdOffsetRange) = r
113113

114+
# Constructor to make `show` round-trippable
115+
function IdOffsetRange(; values::AbstractUnitRange{<:Integer}, indices::AbstractUnitRange{<:Integer})
116+
length(values) == length(indices) || throw(ArgumentError("values and indices must have the same length"))
117+
offset = first(indices) - 1
118+
return IdOffsetRange(values .- offset, offset)
119+
end
120+
114121
# TODO: uncomment these when Julia is ready
115-
# # Conversion preserves both the values and the indexes, throwing an InexactError if this
122+
# # Conversion preserves both the values and the indices, throwing an InexactError if this
116123
# # is not possible.
117124
# Base.convert(::Type{IdOffsetRange{T,I}}, r::IdOffsetRange{T,I}) where {T<:Integer,I<:AbstractUnitRange{T}} = r
118125
# Base.convert(::Type{IdOffsetRange{T,I}}, r::IdOffsetRange) where {T<:Integer,I<:AbstractUnitRange{T}} =
@@ -175,7 +182,7 @@ Broadcast.broadcasted(::Base.Broadcast.DefaultArrayStyle{1}, ::typeof(+), r::IdO
175182
Broadcast.broadcasted(::Base.Broadcast.DefaultArrayStyle{1}, ::typeof(+), x::Integer, r::IdOffsetRange{T}) where T =
176183
IdOffsetRange{T}(x .+ r.parent, r.offset)
177184

178-
Base.show(io::IO, r::IdOffsetRange) = print(io, "OffsetArrays.IdOffsetRange(",first(r), ':', last(r),")")
185+
Base.show(io::IO, r::IdOffsetRange) = print(io, IdOffsetRange, "(values=",first(r), ':', last(r),", indices=",first(eachindex(r)),':',last(eachindex(r)), ")")
179186

180187
# Optimizations
181188
@inline Base.checkindex(::Type{Bool}, inds::IdOffsetRange, i::Real) = Base.checkindex(Bool, inds.parent, i - inds.offset)

test/runtests.jl

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,18 @@ end
163163
@test same_value(r2, 2:4)
164164
check_indexed_by(r2, 2:4)
165165

166+
# Constructor that's round-trippable with `show`
167+
rrt = IdOffsetRange(values=7:9, indices=-1:1)
168+
@test same_value(rrt, 7:9)
169+
check_indexed_by(rrt, -1:1)
170+
@test_throws ArgumentError IdOffsetRange(values=7:9, indices=-1:2)
171+
@test_throws ArgumentError IdOffsetRange(values=7:9, indices=-1:0)
172+
@test_throws TypeError IdOffsetRange(values=7:9, indices=-1)
173+
@test_throws UndefKeywordError IdOffsetRange(values=7:9)
174+
@test_throws UndefKeywordError IdOffsetRange(indices=-1:1)
175+
@test_throws MethodError IdOffsetRange(7:9, indices=-1:1)
176+
@test_throws MethodError IdOffsetRange(-1:1, values=7:9)
177+
166178
# conversion preserves both the values and the axes, throwing an error if this is not possible
167179
@test @inferred(oftype(ro, ro)) === ro
168180
@test @inferred(convert(OffsetArrays.IdOffsetRange{Int}, ro)) === ro
@@ -1215,9 +1227,12 @@ end
12151227
a = OffsetArray([1 2; 3 4], -1:0, 5:6)
12161228
io = IOBuffer()
12171229
show(io, axes(a, 1))
1218-
@test String(take!(io)) == "OffsetArrays.IdOffsetRange(-1:0)"
1230+
@test String(take!(io)) == "IdOffsetRange(values=-1:0, indices=-1:0)" # not qualified because of the using OffsetArrays: IdOffsetRange at top
12191231
show(io, axes(a, 2))
1220-
@test String(take!(io)) == "OffsetArrays.IdOffsetRange(5:6)"
1232+
@test String(take!(io)) == "IdOffsetRange(values=5:6, indices=5:6)"
1233+
rrtable = IdOffsetRange(values=7:9, indices=-1:1)
1234+
rrted = eval(Meta.parse(string(rrtable)))
1235+
@test pairs(rrtable) == pairs(rrted)
12211236

12221237
@test Base.inds2string(axes(a)) == Base.inds2string(map(UnitRange, axes(a)))
12231238

0 commit comments

Comments
 (0)