diff --git a/test/TestHelpers.jl b/test/TestHelpers.jl index b57b0d10f77ee..fef9a3089d6aa 100644 --- a/test/TestHelpers.jl +++ b/test/TestHelpers.jl @@ -43,4 +43,101 @@ function with_fake_pty(f) close(master) end +# OffsetArrays (arrays with indexing that doesn't start at 1) + +# This test file is designed to exercise support for generic indexing, +# even though offset arrays aren't implemented in Base. + +module OAs + +using Base: Indices, LinearSlow, LinearFast, tail + +export OffsetArray + +immutable OffsetArray{T,N,AA<:AbstractArray} <: AbstractArray{T,N} + parent::AA + offsets::NTuple{N,Int} +end +typealias OffsetVector{T,AA<:AbstractArray} OffsetArray{T,1,AA} + +OffsetArray{T,N}(A::AbstractArray{T,N}, offsets::NTuple{N,Int}) = OffsetArray{T,N,typeof(A)}(A, offsets) +OffsetArray{T,N}(A::AbstractArray{T,N}, offsets::Vararg{Int,N}) = OffsetArray(A, offsets) + +(::Type{OffsetArray{T,N}}){T,N}(inds::Indices{N}) = OffsetArray{T,N,Array{T,N}}(Array{T,N}(map(length, inds)), map(indsoffset, inds)) +(::Type{OffsetArray{T}}){T,N}(inds::Indices{N}) = OffsetArray{T,N}(inds) + +Base.linearindexing{T<:OffsetArray}(::Type{T}) = Base.linearindexing(parenttype(T)) +parenttype{T,N,AA}(::Type{OffsetArray{T,N,AA}}) = AA +parenttype(A::OffsetArray) = parenttype(typeof(A)) + +Base.parent(A::OffsetArray) = A.parent + +errmsg(A) = error("size not supported for arrays with indices $(indices(A)); see http://docs.julialang.org/en/latest/devdocs/offset-arrays/") +Base.size(A::OffsetArray) = errmsg(A) +Base.size(A::OffsetArray, d) = errmsg(A) +Base.eachindex(::LinearSlow, A::OffsetArray) = CartesianRange(indices(A)) +Base.eachindex(::LinearFast, A::OffsetVector) = indices(A, 1) + +# Implementations of indices and indices1. Since bounds-checking is +# performance-critical and relies on indices, these are usually worth +# optimizing thoroughly. +@inline Base.indices(A::OffsetArray, d) = 1 <= d <= length(A.offsets) ? indices(parent(A))[d] + A.offsets[d] : (1:1) +@inline Base.indices(A::OffsetArray) = _indices(indices(parent(A)), A.offsets) # would rather use ntuple, but see #15276 +@inline _indices(inds, offsets) = (inds[1]+offsets[1], _indices(tail(inds), tail(offsets))...) +_indices(::Tuple{}, ::Tuple{}) = () +Base.indices1{T}(A::OffsetArray{T,0}) = 1:1 # we only need to specialize this one + +function Base.similar(A::OffsetArray, T::Type, dims::Dims) + B = similar(parent(A), T, dims) +end +function Base.similar(A::AbstractArray, T::Type, inds::Tuple{UnitRange,Vararg{UnitRange}}) + B = similar(A, T, map(length, inds)) + OffsetArray(B, map(indsoffset, inds)) +end + +Base.similar(f::Union{Function,DataType}, shape::Tuple{UnitRange,Vararg{UnitRange}}) = OffsetArray(f(map(length, shape)), map(indsoffset, shape)) + +Base.reshape(A::AbstractArray, inds::Tuple{UnitRange,Vararg{UnitRange}}) = OffsetArray(reshape(A, map(length, inds)), map(indsoffset, inds)) + +@inline function Base.getindex{T,N}(A::OffsetArray{T,N}, I::Vararg{Int,N}) + checkbounds(A, I...) + @inbounds ret = parent(A)[offset(A.offsets, I)...] + ret +end +@inline function Base._getindex(::LinearFast, A::OffsetVector, i::Int) + checkbounds(A, i) + @inbounds ret = parent(A)[offset(A.offsets, (i,))[1]] + ret +end +@inline function Base._getindex(::LinearFast, A::OffsetArray, i::Int) + checkbounds(A, i) + @inbounds ret = parent(A)[i] + ret +end +@inline function Base.setindex!{T,N}(A::OffsetArray{T,N}, val, I::Vararg{Int,N}) + checkbounds(A, I...) + @inbounds parent(A)[offset(A.offsets, I)...] = val + val +end +@inline function Base._setindex!(::LinearFast, A::OffsetVector, val, i::Int) + checkbounds(A, i) + @inbounds parent(A)[offset(A.offsets, (i,))[1]] = val + val +end +@inline function Base._setindex!(::LinearFast, A::OffsetArray, val, i::Int) + checkbounds(A, i) + @inbounds parent(A)[i] = val + val +end + +# Computing a shifted index (subtracting the offset) +offset{N}(offsets::NTuple{N,Int}, inds::NTuple{N,Int}) = _offset((), offsets, inds) +_offset(out, ::Tuple{}, ::Tuple{}) = out +@inline _offset(out, offsets, inds) = _offset((out..., inds[1]-offsets[1]), Base.tail(offsets), Base.tail(inds)) + +indsoffset(r::Range) = first(r) - 1 +indsoffset(i::Integer) = 0 + +end + end diff --git a/test/offsetarray.jl b/test/offsetarray.jl index dac5aad7995ca..2136e4dd580e0 100644 --- a/test/offsetarray.jl +++ b/test/offsetarray.jl @@ -1,103 +1,7 @@ # This file is a part of Julia. License is MIT: http://julialang.org/license -# OffsetArrays (arrays with indexing that doesn't start at 1) - -# This test file is designed to exercise support for generic indexing, -# even though offset arrays aren't implemented in Base. - -module OAs - -using Base: Indices, LinearSlow, LinearFast, tail - -export OffsetArray - -immutable OffsetArray{T,N,AA<:AbstractArray} <: AbstractArray{T,N} - parent::AA - offsets::NTuple{N,Int} -end -typealias OffsetVector{T,AA<:AbstractArray} OffsetArray{T,1,AA} - -OffsetArray{T,N}(A::AbstractArray{T,N}, offsets::NTuple{N,Int}) = OffsetArray{T,N,typeof(A)}(A, offsets) -OffsetArray{T,N}(A::AbstractArray{T,N}, offsets::Vararg{Int,N}) = OffsetArray(A, offsets) - -(::Type{OffsetArray{T,N}}){T,N}(inds::Indices{N}) = OffsetArray{T,N,Array{T,N}}(Array{T,N}(map(length, inds)), map(indsoffset, inds)) -(::Type{OffsetArray{T}}){T,N}(inds::Indices{N}) = OffsetArray{T,N}(inds) - -Base.linearindexing{T<:OffsetArray}(::Type{T}) = Base.linearindexing(parenttype(T)) -parenttype{T,N,AA}(::Type{OffsetArray{T,N,AA}}) = AA -parenttype(A::OffsetArray) = parenttype(typeof(A)) - -Base.parent(A::OffsetArray) = A.parent - -errmsg(A) = error("size not supported for arrays with indices $(indices(A)); see http://docs.julialang.org/en/latest/devdocs/offset-arrays/") -Base.size(A::OffsetArray) = errmsg(A) -Base.size(A::OffsetArray, d) = errmsg(A) -Base.eachindex(::LinearSlow, A::OffsetArray) = CartesianRange(indices(A)) -Base.eachindex(::LinearFast, A::OffsetVector) = indices(A, 1) - -# Implementations of indices and indices1. Since bounds-checking is -# performance-critical and relies on indices, these are usually worth -# optimizing thoroughly. -@inline Base.indices(A::OffsetArray, d) = 1 <= d <= length(A.offsets) ? indices(parent(A))[d] + A.offsets[d] : (1:1) -@inline Base.indices(A::OffsetArray) = _indices(indices(parent(A)), A.offsets) # would rather use ntuple, but see #15276 -@inline _indices(inds, offsets) = (inds[1]+offsets[1], _indices(tail(inds), tail(offsets))...) -_indices(::Tuple{}, ::Tuple{}) = () -Base.indices1{T}(A::OffsetArray{T,0}) = 1:1 # we only need to specialize this one - -function Base.similar(A::OffsetArray, T::Type, dims::Dims) - B = similar(parent(A), T, dims) -end -function Base.similar(A::AbstractArray, T::Type, inds::Tuple{UnitRange,Vararg{UnitRange}}) - B = similar(A, T, map(length, inds)) - OffsetArray(B, map(indsoffset, inds)) -end - -Base.similar(f::Union{Function,DataType}, shape::Tuple{UnitRange,Vararg{UnitRange}}) = OffsetArray(f(map(length, shape)), map(indsoffset, shape)) - -Base.reshape(A::AbstractArray, inds::Tuple{UnitRange,Vararg{UnitRange}}) = OffsetArray(reshape(A, map(length, inds)), map(indsoffset, inds)) - -@inline function Base.getindex{T,N}(A::OffsetArray{T,N}, I::Vararg{Int,N}) - checkbounds(A, I...) - @inbounds ret = parent(A)[offset(A.offsets, I)...] - ret -end -@inline function Base._getindex(::LinearFast, A::OffsetVector, i::Int) - checkbounds(A, i) - @inbounds ret = parent(A)[offset(A.offsets, (i,))[1]] - ret -end -@inline function Base._getindex(::LinearFast, A::OffsetArray, i::Int) - checkbounds(A, i) - @inbounds ret = parent(A)[i] - ret -end -@inline function Base.setindex!{T,N}(A::OffsetArray{T,N}, val, I::Vararg{Int,N}) - checkbounds(A, I...) - @inbounds parent(A)[offset(A.offsets, I)...] = val - val -end -@inline function Base._setindex!(::LinearFast, A::OffsetVector, val, i::Int) - checkbounds(A, i) - @inbounds parent(A)[offset(A.offsets, (i,))[1]] = val - val -end -@inline function Base._setindex!(::LinearFast, A::OffsetArray, val, i::Int) - checkbounds(A, i) - @inbounds parent(A)[i] = val - val -end - -# Computing a shifted index (subtracting the offset) -offset{N}(offsets::NTuple{N,Int}, inds::NTuple{N,Int}) = _offset((), offsets, inds) -_offset(out, ::Tuple{}, ::Tuple{}) = out -@inline _offset(out, offsets, inds) = _offset((out..., inds[1]-offsets[1]), Base.tail(offsets), Base.tail(inds)) - -indsoffset(r::Range) = first(r) - 1 -indsoffset(i::Integer) = 0 - -end - -using OAs +isdefined(:TestHelpers) || include(joinpath(dirname(@__FILE__), "TestHelpers.jl")) +using TestHelpers.OAs let # Basics @@ -219,11 +123,11 @@ cmp_showf(Base.print_matrix, io, OffsetArray(rand(5,5), (10,-9))) # rows&c cmp_showf(Base.print_matrix, io, OffsetArray(rand(10^3,5), (10,-9))) # columns fit cmp_showf(Base.print_matrix, io, OffsetArray(rand(5,10^3), (10,-9))) # rows fit cmp_showf(Base.print_matrix, io, OffsetArray(rand(10^3,10^3), (10,-9))) # neither fits -targets1 = ["0-dimensional OAs.OffsetArray{Float64,0,Array{Float64,0}}:\n1.0", - "OAs.OffsetArray{Float64,1,Array{Float64,1}} with indices 2:2:\n 1.0", - "OAs.OffsetArray{Float64,2,Array{Float64,2}} with indices 2:2×3:3:\n 1.0", - "OAs.OffsetArray{Float64,3,Array{Float64,3}} with indices 2:2×3:3×4:4:\n[:, :, 4] =\n 1.0", - "OAs.OffsetArray{Float64,4,Array{Float64,4}} with indices 2:2×3:3×4:4×5:5:\n[:, :, 4, 5] =\n 1.0"] +targets1 = ["0-dimensional TestHelpers.OAs.OffsetArray{Float64,0,Array{Float64,0}}:\n1.0", + "TestHelpers.OAs.OffsetArray{Float64,1,Array{Float64,1}} with indices 2:2:\n 1.0", + "TestHelpers.OAs.OffsetArray{Float64,2,Array{Float64,2}} with indices 2:2×3:3:\n 1.0", + "TestHelpers.OAs.OffsetArray{Float64,3,Array{Float64,3}} with indices 2:2×3:3×4:4:\n[:, :, 4] =\n 1.0", + "TestHelpers.OAs.OffsetArray{Float64,4,Array{Float64,4}} with indices 2:2×3:3×4:4×5:5:\n[:, :, 4, 5] =\n 1.0"] targets2 = ["(1.0,1.0)", "([1.0],[1.0])", "(\n[1.0],\n\n[1.0])",