diff --git a/base/arrayviews.jl b/base/arrayviews.jl new file mode 100644 index 0000000000000..f422f0f5bd8a4 --- /dev/null +++ b/base/arrayviews.jl @@ -0,0 +1,686 @@ +module ArrayViews + +import Base: eltype, ndims, size, length, stride, strides +import Base: to_index, getindex, setindex!, parent, similar +import Base: convert, Ptr, pointer + +export ArrayView, ContiguousView, StridedView +export contiguous_view, strided_view, view +export iscontiguous, contiguousrank + +#### View types + +# type parameters: +# T: element type +# N: the number of dimensions +# M: contiguous rank +# +# Note: M is the maximum dimension such that +# slices up to this dimension are contiguous. +# +# For example, given a 3D contiguous array a, +# the contiguous rank of a is 3, that of +# view(a, :, :, 1:3) is 2, and that of +# view(a, :, 1:2, 1:3) is 1, etc. +# +# This rank plays a key role in efficient +# linear indexing and type-stable subview +# calculation. +# +abstract ArrayView{T,N,M} <: DenseArray{T,N} + +# a type for indicating contiguous rank (statically) +type ContRank{M} end + +# use ContiguousView when contiguousness can be determined statically +immutable ContiguousView{T,N,Arr<:Array{T}} <: ArrayView{T,N,N} + arr::Arr + offset::Int + len::Int + shp::NTuple{N,Int} +end + +contiguous_view{T,N}(arr::Array{T}, offset::Int, shp::NTuple{N,Int}) = + ContiguousView{T,N,typeof(arr)}(arr, offset, *(shp...), shp) + +contiguous_view(arr::Array, shp::Dims) = contiguous_view(arr, 0, shp) + +# use StridedView otherwise +# condition: M <= N +immutable StridedView{T,N,M,Arr<:Array{T}} <: ArrayView{T,N,M} + arr::Arr + offset::Int + len::Int + shp::NTuple{N,Int} + strides::NTuple{N,Int} +end + +function strided_view{T,N,M}(arr::Array{T}, offset::Int, shp::NTuple{N,Int}, + ::Type{ContRank{M}}, strides::NTuple{N,Int}) + @assert M <= N + StridedView{T,N,M,typeof(arr)}(arr, offset, *(shp...), shp, strides) +end + +function strided_view{T,N,M}(arr::Array{T}, shp::NTuple{N,Int}, + ::Type{ContRank{M}}, strides::NTuple{N,Int}) + @assert M <= N + StridedView{T,N,M,typeof(arr)}(arr, 0, *(shp...), shp, strides) +end + + +#### Basic methods + +eltype{T}(a::ArrayView{T}) = T +ndims{T,N}(a::ArrayView{T,N}) = N + +contiguousrank{T,N,M}(a::ArrayView{T,N,M}) = M +contrank{T,N}(a::Array{T,N}) = ContRank{N} +contrank{T,N,M}(a::ArrayView{T,N,M}) = ContRank{M} + +getdim{N}(s::NTuple{N,Int}, d::Integer) = + (d > 0 || error("dimension out of range."); d <= N ? s[d] : 1) +size{T,N}(a::ArrayView{T,N}, d::Integer) = getdim(size(a), d) + +pointer(a::ArrayView) = pointer(parent(a), offset(a)+1) +convert{T}(::Type{Ptr{T}}, a::ArrayView{T}) = pointer(a) + +similar{T}(a::ArrayView{T}) = Array(T, size(a)) +similar{T}(a::ArrayView{T}, dims::Dims) = Array(T, dims) +similar{T}(a::ArrayView, ::Type{T}, dims::Dims) = Array(T, dims) + +# methods specific to ContiguousView + +parent(a::ContiguousView) = a.arr +offset(a::ContiguousView) = a.offset +length(a::ContiguousView) = a.len +size(a::ContiguousView) = a.shp +iscontiguous(a::ContiguousView) = true; + +strides{T}(a::ContiguousView{T,1}) = (1,) +strides{T}(a::ContiguousView{T,2}) = (1, a.shp[1]) +strides{T}(a::ContiguousView{T,3}) = (1, a.shp[1], a.shp[1] * a.shp[2]) +strides{T}(a::ContiguousView{T,4}) = + (1, a.shp[1], a.shp[1] * a.shp[2], a.shp[1] * a.shp[2] * a.shp[3]) + +function strides{T,N}(a::ContiguousView{T,N}) + s = Array(Int, N) + s[1] = p = 1 + d = 1 + while d < N + p *= a.shp[d] + d += 1 + s[d] = p + end + return tuple(s...)::NTuple{N,Int} +end + +stride{T}(a::ContiguousView{T,1}, d::Integer) = + (d > 0 || error("dimension out of range."); + d == 1 ? 1 : length(a))::Int + +stride{T}(a::ContiguousView{T,2}, d::Integer) = + (d > 0 || error("dimension out of range."); + d == 1 ? 1 : + d == 2 ? a.shp[1] : length(a))::Int + +stride{T}(a::ContiguousView{T,3}, d::Integer) = + (d > 0 || error("dimension out of range."); + d == 1 ? 1 : + d == 2 ? a.shp[1] : + d == 3 ? a.shp[1] * a.shp[2] : length(a))::Int + +stride{T,N}(a::ContiguousView{T,N}, d::Integer) = + (d > 0 || error("dimension out of range."); + d == 1 ? 1 : + d == 2 ? a.shp[1] : + d <= N ? *(a.shp[1:d-1]...) : length(a))::Int + +# methods specific to StridedView + +parent(a::StridedView) = a.arr +offset(a::StridedView) = a.offset +length(a::StridedView) = a.len +size(a::StridedView) = a.shp + +strides(a::StridedView) = a.strides +stride{T,N}(a::StridedView{T,N}, d::Integer) = + (1 <= d <= N || error("dimension out of range."); + a.strides[d]) + +iscontiguous{T,N}(a::StridedView{T,N,N}) = true; +iscontiguous{T,N}(a::StridedView{T,N,N}) = true; +iscontiguous{T,N}(a::StridedView{T,N}) = _iscontiguous(a.shp, a.strides) + +_iscontiguous(shp::(), strides::()) = true +_iscontiguous(shp::(Int,), strides::(Int,)) = (strides[1] == 1) +_iscontiguous(shp::(Int,Int), strides::(Int,Int)) = + (strides[1] == 1 && strides[2] == shp[1]) +_iscontiguous(shp::(Int,Int,Int), strides::(Int,Int,Int)) = + (strides[1] == 1 && + strides[2] == shp[1] && + strides[3] == shp[1] * shp[2]) + +function _iscontiguous{N}(shp::NTuple{N,Int}, strides::NTuple{N,Int}) + s = 1 + for i = 1:N + if strides[i] != s + return false + end + s *= shp[i] + end + return true +end + + +#### Indexing + +# Note: each subtype of ArrayView should implement uindex methods, +# which getindex and setindex! rely on to locate the element position + +# getindex + +getindex(a::ArrayView, i::Int) = arrayref(a.arr, uindex(a, i)) +getindex(a::ArrayView, i1::Int, i2::Int) = arrayref(a.arr, uindex(a, i1, i2)) +getindex(a::ArrayView, i1::Int, i2::Int, i3::Int) = + arrayref(a.arr, uindex(a, i1, i2, i3)) +getindex(a::ArrayView, i1::Int, i2::Int, i3::Int, i4::Int) = + arrayref(a.arr, uindex(a, i1, i2, i3, i4)) +getindex(a::ArrayView, i1::Int, i2::Int, i3::Int, i4::Int, i5::Int) = + arrayref(a.arr, uindex(a, i1, i2, i3, i4, i5)) +getindex(a::ArrayView, i1::Int, i2::Int, i3::Int, i4::Int, i5::Int, i6::Int, I::Int...) = + arrayref(a.arr, uindex(a, i1, i2, i3, i4, i5, i6, I...)) + +getindex(a::ArrayView, i::Real) = getindex(a, to_index(i)) +getindex(a::ArrayView, i1::Real, i2::Real) = getindex(a, to_index(i1), to_index(i2)) +getindex(a::ArrayView, i1::Real, i2::Real, i3::Real) = + getindex(a, to_index(i1), to_index(i2), to_index(i3)) +getindex(a::ArrayView, i1::Real, i2::Real, i3::Real, i4::Real) = + getindex(a, to_index(i1), to_index(i2), to_index(i3), to_index(i4)) +getindex(a::ArrayView, i1::Real, i2::Real, i3::Real, i4::Real, i5::Real) = + getindex(a, to_index(i1), to_index(i2), to_index(i3), to_index(i4), to_index(i5)) +getindex(a::ArrayView, i1::Real, i2::Real, i3::Real, i4::Real, i5::Real, i6::Real, I::Int...) = + getindex(a, to_index(i1), to_index(i2), to_index(i3), + to_index(i4), to_index(i5), to_index(i6), I...) + +# setindex! + +setindex!{T}(a::ArrayView{T}, v, i::Int) = arrayset(a.arr, convert(T, v), uindex(a, i)) +setindex!{T}(a::ArrayView{T}, v, i1::Int, i2::Int) = + arrayset(a.arr, convert(T, v), uindex(a, i1, i2)) +setindex!{T}(a::ArrayView{T}, v, i1::Int, i2::Int, i3::Int) = + arrayset(a.arr, convert(T, v), uindex(a, i1, i2, i3)) +setindex!{T}(a::ArrayView{T}, v, i1::Int, i2::Int, i3::Int, i4::Int) = + arrayset(a.arr, convert(T, v), uindex(a, i1, i2, i3, i4)) +setindex!{T}(a::ArrayView{T}, v, i1::Int, i2::Int, i3::Int, i4::Int, i5::Int) = + arrayset(a.arr, convert(T, v), uindex(a, i1, i2, i3, i4, i5)) +setindex!{T}(a::ArrayView{T}, v, i1::Int, i2::Int, i3::Int, i4::Int, i5::Int, i6::Int, I::Int...) = + arrayset(a.arr, convert(T, v), uindex(a, i1, i2, i3, i4, i5, i6, I...)) + +setindex!(a::ArrayView, v, i::Real) = setindex!(a, v, to_index(i)) +setindex!(a::ArrayView, v, i1::Real, i2::Real) = setindex!(a, v, to_index(i1), to_index(i2)) +setindex!(a::ArrayView, v, i1::Real, i2::Real, i3::Real) = + setindex!(a, v, to_index(i1), to_index(i2), to_index(i3)) +setindex!(a::ArrayView, v, i1::Real, i2::Real, i3::Real, i4::Real) = + setindex!(a, v, to_index(i1), to_index(i2), to_index(i3), to_index(i4)) +setindex!(a::ArrayView, v, i1::Real, i2::Real, i3::Real, i4::Real, i5::Real) = + setindex!(a, v, to_index(i1), to_index(i2), to_index(i3), to_index(i4), to_index(i5)) +setindex!(a::ArrayView, v, i1::Real, i2::Real, i3::Real, i4::Real, i5::Real, i6::Real) = + setindex!(a, v, to_index(i1), to_index(i2), to_index(i3), + to_index(i4), to_index(i5), to_index(i6)) +setindex!(a::ArrayView, v, i1::Real, i2::Real, i3::Real, i4::Real, i5::Real, i6::Real, I::Int...) = + setindex!(a, v, to_index(i1), to_index(i2), to_index(i3), + to_index(i4), to_index(i5), to_index(i6), I...) + +# index calculation for ContiguousView + +uindex{T,N}(a::ArrayView{T,N,N}, i::Int) = a.offset + i +uindex{T,N}(a::ArrayView{T,N,N}, i1::Int, i2::Int) = + a.offset + sub2ind(size(a), i1, i2) +uindex{T,N}(a::ArrayView{T,N,N}, i1::Int, i2::Int, i3::Int) = + a.offset + sub2ind(size(a), i1, i2, i3) +uindex{T,N}(a::ArrayView{T,N,N}, i1::Int, i2::Int, i3::Int, i4::Int, I::Int...) = + a.offset + sub2ind(size(a), i1, i2, i3, i4, I...) + +# index calculation for StridedView + +uindex{T}(a::StridedView{T,0}, i::Int) = 1 + +uindex{T}(a::StridedView{T,1,0}, i::Int) = a.offset + 1 + (i-1)*a.strides[1] +uindex{T}(a::StridedView{T,1,1}, i::Int) = a.offset + i +uindex{T}(a::StridedView{T,1}, i1::Int, i2::Int) = + (i2 == 1 || throw(BoundsError()); uindex(a, i1)) +uindex{T}(a::StridedView{T,1}, i1::Int, i2::Int, i3::Int) = + ((i2 == i3 == 1) || throw(BoundsError()); uindex(a, i1)) + +uindex{T}(a::StridedView{T,2}, i::Int) = uindex(a, ind2sub(size(a), i)...) +uindex{T}(a::StridedView{T,2,0}, i1::Int, i2::Int) = + a.offset + 1 + (i1-1)*a.strides[1] + (i2-1)*a.strides[2] +uindex{T}(a::StridedView{T,2,1}, i1::Int, i2::Int) = + a.offset + i1 + (i2-1)*a.strides[2] +uindex{T}(a::StridedView{T,2,2}, i1::Int, i2::Int) = + a.offset + i1 + (i2-1)*a.strides[2] +uindex{T}(a::StridedView{T,2}, i1::Int, i2::Int, i3::Int) = + (i3 == 1 || throw(BoundsError()); uindex(a, i1, i2)) + +uindex{T}(a::StridedView{T,3}, i::Int) = uindex(a, ind2sub(size(a), i)...) +uindex{T}(a::StridedView{T,3}, i1::Int, i2::Int) = + uindex(a, i1, ind2sub((a.shp[2], a.shp[3]), i2)...) +uindex{T}(a::StridedView{T,3}, i1::Int, i2::Int, i3::Int) = + a.offset + i1 + (i2-1)*a.strides[2] + (i3-1)*a.strides[3] +uindex{T}(a::StridedView{T,3,0}, i1::Int, i2::Int, i3::Int) = + a.offset + 1 + (i1-1)*a.strides[1] + (i2-1)*a.strides[2] + (i3-1)*a.strides[3] + +uindex(a::StridedView, i::Int) = uindex(a, ind2sub(size(a), i)...) +uindex{T,N}(a::StridedView{T,N}, i1::Int, i2::Int) = + uindex(a, i1, ind2sub(a.shp[2:N], i2)...) +uindex{T,N}(a::StridedView{T,N}, i1::Int, i2::Int, i3::Int) = + uindex(a, i1, i2, ind2sub(a.shp[3:N], i3)...) +uindex{T}(a::StridedView{T}, i1::Int, i2::Int, i3::Int, i4::Int, I::Int...) = + _uindex(a, tuple(i1, i2, i3, i4, I...))::Int + +function _uindex{T,N,L}(a::StridedView{T,N}, subs::NTuple{L,Int}) + if L == N + s = a.offset + 1 + for i = 1:N + s += (subs[i] - 1) * a.strides[i] + end + return s + + elseif L < N + return uindex(a, tuple(subs[1:L-1]..., ind2sub(a.shp[L+1:N], subs[L])...)) + + else # L > N + for i = N+1:L + subs[i] == 1 || throw(BoundsError()) + end + return uindex(a, subs[1:N]...) + end +end + + +#### Subviews + +# Note: construction of subviews involve several steps: +# +# - compute view offset (w.r.t. the parent) +# - compute view shape +# - compute strides (for non-contiguous views only) +# - decide contiguous rank (statically) +# - make the view +# + +## auxiliary union types to simplify method definition +## (for internal use only) + +typealias Indexer Union(Real,Range1,Range) +typealias Subs Union(Real,Colon,Range1,Range) +typealias CSubs Union(Real,Colon,Range1) +typealias SubsRange Union(Colon,Range1,Range) +typealias CSubsRange Union(Colon,Range1) + +### Compute offset + +_offset(i::Colon) = 0 +_offset(i::Int) = i - 1 +_offset(i::Real) = to_index(i) - 1 +_offset(i::Ranges) = to_index(first(i)) - 1 + +_step(i::Real) = 1 +_step(i::Colon) = 1 +_step(i::Ranges) = step(i) + +# aoffset: offset w.r.t. the underlying array (i.e. parent) + +aoffset(a::Array, i::Subs) = roffset(a, i) +aoffset(a::Array, i1::Subs, i2::Subs) = roffset(a, i1, i2) +aoffset(a::Array, i1::Subs, i2::Subs, i3::Subs) = roffset(a, i1, i2, i3) +aoffset(a::Array, i1::Subs, i2::Subs, i3::Subs, i4::Subs) = + roffset(a, i1, i2, i3, i4) +aoffset(a::Array, i1::Subs, i2::Subs, i3::Subs, i4::Subs, i5::Subs, I::Subs...) = + roffset(a, i1, i2, i3, i4, i5, I...) + +aoffset(a::ArrayView, i::Subs) = a.offset + roffset(a, i) +aoffset(a::ArrayView, i1::Subs, i2::Subs) = a.offset + roffset(a, i1, i2) +aoffset(a::ArrayView, i1::Subs, i2::Subs, i3::Subs) = + a.offset + roffset(a, i1, i2, i3) +aoffset(a::ArrayView, i1::Subs, i2::Subs, i3::Subs, i4::Subs) = + a.offset + roffset(a, i1, i2, i3, i4) +aoffset(a::ArrayView, i1::Subs, i2::Subs, i3::Subs, i4::Subs, i5::Subs, I::Subs...) = + a.offset + roffset(a, i1, i2, i3, i4, i5, I...) + +# roffset: offset w.r.t. the first element of the view + +# for contiguous arrays + +typealias ContiguousArray{T,N} Union(Array{T,N}, ContiguousView{T,N}) + +roffset(a::ContiguousArray, i::Colon) = 0 +roffset(a::ContiguousArray, i::Indexer) = _offset(i) + +roffset(a::ContiguousArray, i1::Colon, i2::Colon) = 0 +roffset(a::ContiguousArray, i1::Colon, i2::Indexer) = size(a,1) * _offset(i2) +roffset(a::ContiguousArray, i1::Indexer, i2::Colon) = _offset(i1) +roffset(a::ContiguousArray, i1::Indexer, i2::Indexer) = + _offset(i1) + size(a,1) * _offset(i2) + +roffset(a::ContiguousArray, i1::Colon, i2::Colon, i3::Colon) = 0 +roffset(a::ContiguousArray, i1::Colon, i2::Colon, i3::Indexer) = + size(a,1) * size(a,2) * _offset(i3) +roffset(a::ContiguousArray, i1::Colon, i2::Indexer, i3::Colon) = + size(a,1) * _offset(i2) +roffset(a::ContiguousArray, i1::Colon, i2::Indexer, i3::Indexer) = + size(a,1) * (_offset(i2) + size(a,2) * _offset(i3)) +roffset(a::ContiguousArray, i1::Indexer, i2::Colon, i3::Colon) = _offset(i1) +roffset(a::ContiguousArray, i1::Indexer, i2::Colon, i3::Indexer) = + _offset(i1) + (size(a,1) * size(a,2) * _offset(i3)) +roffset(a::ContiguousArray, i1::Indexer, i2::Indexer, i3::Colon) = + _offset(i1) + size(a,1) * _offset(i2) +roffset(a::ContiguousArray, i1::Indexer, i2::Indexer, i3::Indexer) = + _offset(i1) + size(a,1) * (_offset(i2) + size(a,2) * _offset(i3)) + +roffset(a::ContiguousArray, i1::Colon, i2::Colon, i3::Colon, i4::Colon, I::Colon...) = 0 + +function roffset(a::ContiguousArray, i1::Subs, i2::Subs, i3::Subs, i4::Subs, I::Subs...) + o = _offset(i1) + s = size(a,1) + o += s * _offset(i2) + o += (s *= size(a,2)) * _offset(i3) + o += (s *= size(a,3)) * _offset(i4) + for i = 1:length(I) + o += (s *= size(a,i+3)) * _offset(I[i]) + end + return o::Int +end + +# for strided arrays + +roffset{T}(a::StridedArray{T,1}, i::Colon) = 0 +roffset{T}(a::StridedArray{T,1}, i::Indexer) = _offset(i) * stride(a,1) + +roffset{T}(a::StridedArray{T,2}, i1::Colon, i2::Colon) = 0 +roffset{T}(a::StridedArray{T,2}, i1::Colon, i2::Indexer) = _offset(i2) * stride(a,2) +roffset{T}(a::StridedArray{T,2}, i1::Indexer, i2::Colon) = _offset(i1) * stride(a,1) +roffset{T}(a::StridedArray{T,2}, i1::Indexer, i2::Indexer) = + _offset(i1) * stride(a,1) + _offset(i2) * stride(a,2) + +roffset{T}(a::StridedArray{T,3}, i1::Colon, i2::Colon, i3::Colon) = 0 +roffset{T}(a::StridedArray{T,3}, i1::Colon, i2::Colon, i3::Indexer) = + _offset(i3) * stride(a,3) +roffset{T}(a::StridedArray{T,3}, i1::Colon, i2::Indexer, i3::Colon) = + _offset(i2) * stride(a,2) +roffset{T}(a::StridedArray{T,3}, i1::Colon, i2::Indexer, i3::Indexer) = + _offset(i2) * stride(a,2) + _offset(i3) * stride(a,3) +roffset{T}(a::StridedArray{T,3}, i1::Indexer, i2::Colon, i3::Colon) = + _offset(i1) * stride(a,1) +roffset{T}(a::StridedArray{T,3}, i1::Indexer, i2::Colon, i3::Indexer) = + _offset(i1) * stride(a,1) + _offset(i3) * stride(a,3) +roffset{T}(a::StridedArray{T,3}, i1::Indexer, i2::Indexer, i3::Colon) = + _offset(i1) * stride(a,1) + _offset(i2) * stride(a,2) +roffset{T}(a::StridedArray{T,3}, i1::Indexer, i2::Indexer, i3::Indexer) = + _offset(i1) * stride(a,1) + _offset(i2) * stride(a,2) + _offset(i3) * stride(a,3) + +roffset(a::StridedArray, i1::Subs, i2::Subs, i3::Subs, I::Subs...) = + _roffset(strides(a), tuple(i1, i2, i3, I...)) + +function _roffset{N}(ss::NTuple{N,Int}, subs::NTuple{N}) + o = _offset(subs[1]) * ss[1] + for i = 2:N + o += _offset(subs[i]) * ss[i] + end + return o::Int +end + + +### Compute view shape + +_dim(a::AbstractArray, d::Int, r::Colon) = size(a, d) +_dim(a::AbstractArray, d::Int, r::Ranges) = length(r) +_dim(a::AbstractArray, d::Int, r::Real) = 1 + +_dim{N}(siz::NTuple{N,Int}, d::Int, r::Colon) = d <= N ? siz[d] : 1 +_dim(siz::Tuple, d::Int, r::Ranges) = length(r) +_dim(siz::Tuple, d::Int, r::Real) = 1 + +# 1D +vshape(a::DenseArray, i::Real) = () +vshape(a::DenseArray, i::Colon) = (length(a),) +vshape(a::DenseArray, i::Ranges) = (length(i),) + +# 2D + +_succlen2{T}(a::DenseArray{T,1}) = 1 +_succlen2{T}(a::DenseArray{T,2}) = size(a, 2) +_succlen2{T}(a::DenseArray{T,3}) = size(a, 2) * size(a, 3) +_succlen2(a::DenseArray) = prod(size(a)[2:end])::Int + +vshape(a::DenseArray, i1::Real, i2::Real) = () +vshape(a::DenseArray, i1::SubsRange, i2::Real) = (_dim(a,1,i1),) +vshape(a::DenseArray, i1::Subs, i2::Colon) = (_dim(a,1,i1), _succlen2(a)) +vshape(a::DenseArray, i1::Subs, i2::Ranges) = (_dim(a,1,i1), length(i2)) + +# 3D + +_succlen3{T}(a::DenseArray{T,1}) = 1 +_succlen3{T}(a::DenseArray{T,2}) = 1 +_succlen3{T}(a::DenseArray{T,3}) = size(a, 3) +_succlen3{T}(a::DenseArray{T,4}) = size(a, 3) * size(a, 4) +_succlen3(a::DenseArray) = prod(size(a)[3:end])::Int + +vshape(a::DenseArray, i1::Real, i2::Real, i3::Real) = () +vshape(a::DenseArray, i1::SubsRange, i2::Real, i3::Real) = (_dim(a,1,i1),) +vshape(a::DenseArray, i1::Subs, i2::SubsRange, i3::Real) = + (_dim(a,1,i1), _dim(a,2,i2)) + +vshape(a::DenseArray, i1::Subs, i2::Subs, i3::Colon) = + (_dim(a,1,i1), _dim(a,2,i2), _succlen3(a)) +vshape(a::DenseArray, i1::Subs, i2::Subs, i3::Ranges) = + (_dim(a,1,i1), _dim(a,2,i2), length(i3)) + +# multi-dimensional + +vshape{T,N}(a::DenseArray{T,N}, i1::Subs, i2::Subs, i3::Subs, i4::Subs, I::Subs...) = + _vshape(size(a), i1, i2, i3, i4, I...) + +_vshape{N}(siz::NTuple{N,Int}, i1::Real) = () +_vshape{N}(siz::NTuple{N,Int}, i1::Real, i2::Real...) = () + +_vshape{N}(siz::NTuple{N,Int}, i1::Colon) = (prod(siz),) +_vshape{N}(siz::NTuple{N,Int}, i1::Union(Range,Range1)) = (length(i1),) +_vshape{N}(siz::NTuple{N,Int}, i1::Subs, i2::Subs...) = tuple(_dim(siz,1,i1), _vshape(siz[2:N], i2...)...) + + +### Compute strides + +# 1D + +vstrides(a::ContiguousArray, i::Subs) = (_step(i),) +vstrides(a::DenseArray, i::Subs) = (stride(a,1) * _step(i),) + +# 2D + +vstrides(a::ContiguousArray, i1::Subs, i2::Real) = (_step(i1),) +vstrides(a::ContiguousArray, i1::Subs, i2::CSubs) = (_step(i1), stride(a,2)) +vstrides(a::ContiguousArray, i1::Subs, i2::Range) = (_step(i1), stride(a,2) * _step(i2)) + +vstrides(a::DenseArray, i1::Subs, i2::Real) = (stride(a,1) * _step(i1),) +vstrides(a::DenseArray, i1::Subs, i2::Subs) = + (stride(a,1) * _step(i1), stride(a,2) * _step(i2)) + +# 3D + +vstrides(a::ContiguousArray, i1::Subs, i2::Real, i3::Real) = + (_step(i1),) +vstrides(a::ContiguousArray, i1::Subs, i2::Subs, i3::Real) = + (_step(i1), stride(a,2) * _step(i2)) +vstrides(a::ContiguousArray, i1::Subs, i2::Subs, i3::Subs) = + (_step(i1), stride(a,2) * _step(i2), stride(a,3) * _step(i3)) + +vstrides(a::DenseArray, i1::Subs, i2::Real, i3::Real) = + (stride(a,1) * _step(i1),) +vstrides(a::DenseArray, i1::Subs, i2::Subs, i3::Real) = + (stride(a,1) * _step(i1), stride(a,2) * _step(i2)) +vstrides(a::DenseArray, i1::Subs, i2::Subs, i3::Subs) = + (stride(a,1) * _step(i1), stride(a,2) * _step(i2), stride(a,3) * _step(i3)) + +# multi-dimensional array + +vstrides(a::DenseArray, i1::Subs, i2::Subs, i3::Subs, i4::Subs, I::Subs...) = + _vstrides(strides(a), 1, i1, i2, i3, i4, I...) + +_vstrides{N}(ss::NTuple{N,Int}, k::Int, i1::Real, i2::Real) = () +_vstrides{N}(ss::NTuple{N,Int}, k::Int, i1::Subs, i2::Real) = + (ss[k] * _step(i1),) +_vstrides{N}(ss::NTuple{N,Int}, k::Int, i1::Subs, i2::Subs) = + (ss[k] * _step(i1), ss[k+1] * _step(i2)) + +_vstrides{N}(ss::NTuple{N,Int}, k::Int, i1::Real, i2::Real, i3::Real, I::Real...) = () +_vstrides{N}(ss::NTuple{N,Int}, k::Int, i1::Subs, i2::Subs, i3::Subs, I::Subs...) = + tuple(ss[k] * _step(i1), _vstrides(ss, k+1, i2, i3, I...)...) + + +### Make views + +make_view{N}(a::DenseArray, cr::Type{ContRank{N}}, shp::NTuple{N,Int}, i::Subs) = + contiguous_view(parent(a), aoffset(a, i), shp) + +make_view{N}(a::DenseArray, cr::Type{ContRank{N}}, shp::NTuple{N,Int}, i1::Subs, i2::Subs) = + contiguous_view(parent(a), aoffset(a, i1, i2), shp) + +make_view{N}(a::DenseArray, cr::Type{ContRank{N}}, shp::NTuple{N,Int}, i1::Subs, i2::Subs, i3::Subs) = + contiguous_view(parent(a), aoffset(a, i1, i2, i3), shp) + +make_view{N}(a::DenseArray, cr::Type{ContRank{N}}, shp::NTuple{N,Int}, i1::Subs, i2::Subs, i3::Subs, i4::Subs, I::Subs...) = + contiguous_view(parent(a), aoffset(a, i1, i2, i3, i4, I...), shp) + +make_view{M,N}(a::DenseArray, cr::Type{ContRank{M}}, shp::NTuple{N,Int}, i::Subs) = + strided_view(parent(a), aoffset(a, i), shp, cr, vstrides(a, i)) + +make_view{M,N}(a::DenseArray, cr::Type{ContRank{M}}, shp::NTuple{N,Int}, i1::Subs, i2::Subs) = + strided_view(parent(a), aoffset(a, i1, i2), shp, cr, vstrides(a, i1, i2)) + +make_view{M,N}(a::DenseArray, cr::Type{ContRank{M}}, shp::NTuple{N,Int}, i1::Subs, i2::Subs, i3::Subs) = + strided_view(parent(a), aoffset(a, i1, i2, i3), shp, cr, vstrides(a, i1, i2, i3)) + +make_view{M,N}(a::DenseArray, cr::Type{ContRank{M}}, shp::NTuple{N,Int}, i1::Subs, i2::Subs, i3::Subs, i4::Subs, I::Subs...) = + strided_view(parent(a), aoffset(a, i1, i2, i3, i4, I...), shp, cr, vstrides(a, i1, i2, i3, i4, I...)) + + +view(a::DenseArray, i::Subs) = + (shp = vshape(a, i); make_view(a, restrict_crank(contrank(a, i), shp), shp, i)) + +view(a::DenseArray, i1::Subs, i2::Subs) = + (shp = vshape(a, i1, i2); make_view(a, restrict_crank(contrank(a, i1, i2), shp), shp, i1, i2)) + +view(a::DenseArray, i1::Subs, i2::Subs, i3::Subs) = + (shp = vshape(a, i1, i2, i3); make_view(a, restrict_crank(contrank(a, i1, i2, i3), shp), shp, i1, i2, i3)) + +view(a::DenseArray, i1::Subs, i2::Subs, i3::Subs, i4::Subs, I::Subs...) = + (shp = vshape(a, i1, i2, i3, i4, I...); + make_view(a, restrict_crank(contrank(a, i1, i2, i3, i4, I...), shp), shp, i1, i2, i3, i4, I...)) + + +#### Arithmetics on contiguous ranks + +for m=0:3, n=0:3 + global addrank + @eval addrank(::Type{ContRank{$m}}, ::Type{ContRank{$n}}) = ContRank{$(m+n)} +end + +addrank{M,N}(::Type{ContRank{M}}, ::Type{ContRank{N}}) = ContRank{M+N} +addrank{N}(::Type{ContRank{0}}, ::Type{ContRank{N}}) = ContRank{N} +addrank{N}(::Type{ContRank{N}}, ::Type{ContRank{0}}) = ContRank{N} + +for m=0:3, n=0:3 + global minrank + @eval minrank(::Type{ContRank{$m}}, ::Type{ContRank{$n}}) = ContRank{$(min(m,n))} +end + +minrank{M,N}(::Type{ContRank{M}}, ::Type{ContRank{N}}) = ContRank{min(M,N)} +minrank{N}(::Type{ContRank{0}}, ::Type{ContRank{N}}) = ContRank{0} +minrank{N}(::Type{ContRank{N}}, ::Type{ContRank{0}}) = ContRank{0} + +for m=0:3, n=0:3 + global restrict_crank + @eval restrict_crank(::Type{ContRank{$m}}, ::NTuple{$n,Int}) = ContRank{$(min(m,n))} +end + +restrict_crank{M,N}(::Type{ContRank{M}}, ::NTuple{N,Int}) = ContRank{min(M,N)} +restrict_crank{N}(::Type{ContRank{0}}, ::NTuple{N,Int}) = ContRank{0} +restrict_crank{N}(::Type{ContRank{N}}, ::()) = ContRank{0} + +# contiguous rank computation based on indices + +_nprefixreals() = ContRank{0} +_nprefixreals(i::Real) = ContRank{1} +_nprefixreals(i::SubsRange) = ContRank{0} + +_nprefixreals(i1::Real, i2::Real) = ContRank{2} +_nprefixreals(i1::Real, i2::SubsRange) = ContRank{1} +_nprefixreals(i1::SubsRange, i2::Subs) = ContRank{0} + +_nprefixreals(i1::Real, i2::Real, i3::Real) = ContRank{3} +_nprefixreals(i1::Real, i2::Real, i3::Real, I::Subs...) = + addrank(ContRank{3}, _nprefixreals(I...)) +_nprefixreals(i1::Real, i2::Real, i3::SubsRange, I::Subs...) = ContRank{2} +_nprefixreals(i1::Real, i2::SubsRange, i3::Subs, I::Subs...) = ContRank{1} +_nprefixreals(i1::SubsRange, i2::Subs, i3::Subs, I::Subs...) = ContRank{0} + +contrank() = ContRank{0} + +contrank(i::Real) = ContRank{1} +contrank(i::Real, i2::Real) = ContRank{2} +contrank(i::Real, i2::Real, I::Subs...) = _nprefixreals(i, i2, I...) +contrank(i::Real, i2::SubsRange, I::Subs...) = ContRank{1} + +contrank(i::Range, I::Subs...) = ContRank{0} + +contrank(i1::Colon) = ContRank{1} +contrank(i1::Colon, i2::CSubs) = ContRank{2} +contrank(i1::Colon, i2::Range) = ContRank{1} + +contrank(i1::Colon, i2::Colon, i3::Colon, I::Subs...) = + addrank(ContRank{3}, contrank(I...)) +contrank(i1::Colon, i2::Colon, i3::Real, I::Subs...) = + addrank(ContRank{3}, _nprefixreals(I...)) +contrank(i1::Colon, i2::Colon, i3::Range1, I::Subs...) = + addrank(ContRank{3}, _nprefixreals(I...)) +contrank(i1::Colon, i2::Colon, i3::Range, I::Subs...) = ContRank{2} + +contrank(i1::Colon, i2::Union(Real,Range1), I::Subs...) = addrank(ContRank{2}, _nprefixreals(I...)) +contrank(i1::Colon, i2::Range, I::Subs...) = ContRank{1} + +contrank(i1::Range1) = ContRank{1} +contrank(i1::Range1, i2::Real) = ContRank{2} +contrank(i1::Range1, i2::SubsRange, I::Subs...) = ContRank{1} + +contrank(i1::Range1, i2::Real, i3::Real) = ContRank{3} +contrank(i1::Range1, i2::Real, I::Subs...) = addrank(ContRank{2}, _nprefixreals(I...)) + +# contiguous rank with array & arrayviews + +contrank(a::Array, i1::Subs) = contrank(i1) +contrank(a::Array, i1::Subs, i2::Subs) = contrank(i1, i2) +contrank(a::Array, i1::Subs, i2::Subs, i3::Subs) = contrank(i1, i2, i3) +contrank(a::Array, i1::Subs, i2::Subs, i3::Subs, i4::Subs) = contrank(i1, i2, i3, i4) +contrank(a::Array, i1::Subs, i2::Subs, i3::Subs, i4::Subs, i5::Subs, I::Subs...) = + contrank(i1, i2, i3, i4, i5, I...) + +contrank{T,N}(a::ArrayView{T,N,N}, i1::Subs) = contrank(i1) +contrank{T,N}(a::ArrayView{T,N,N}, i1::Subs, i2::Subs) = contrank(i1, i2) +contrank{T,N}(a::ArrayView{T,N,N}, i1::Subs, i2::Subs, i3::Subs) = contrank(i1, i2, i3) +contrank{T,N}(a::ArrayView{T,N,N}, i1::Subs, i2::Subs, i3::Subs, i4::Subs) = + contrank(i1, i2, i3, i4) +contrank{T,N}(a::ArrayView{T,N,N}, i1::Subs, i2::Subs, i3::Subs, i4::Subs, i5::Subs, I::Subs...) = + contrank(i1, i2, i3, i4, i5, I...) + +contrank{T,N,M}(a::ArrayView{T,N,M}, i1::Subs) = minrank(contrank(i1), ContRank{M}) +contrank{T,N,M}(a::ArrayView{T,N,M}, i1::Subs, i2::Subs) = + minrank(contrank(i1, i2), ContRank{M}) +contrank{T,N,M}(a::ArrayView{T,N,M}, i1::Subs, i2::Subs, i3::Subs) = + minrank(contrank(i1, i2, i3), ContRank{M}) +contrank{T,N,M}(a::ArrayView{T,N,M}, i1::Subs, i2::Subs, i3::Subs, i4::Subs) = + minrank(contrank(i1, i2, i3, i4), ContRank{M}) +contrank{T,N,M}(a::ArrayView{T,N,M}, i1::Subs, i2::Subs, i3::Subs, i4::Subs, i5::Subs, I::Subs...) = + minrank(contrank(i1, i2, i3, i4, i5, I...), ContRank{M}) + +end # module ArrayViews diff --git a/base/exports.jl b/base/exports.jl index cbf4981ef4113..b760210ab1cbb 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -18,6 +18,7 @@ export AbstractVector, AbstractVecOrMat, Array, + ArrayView, Associative, Bidiagonal, BigFloat, @@ -35,6 +36,7 @@ export Complex128, Complex64, Complex32, + ContiguousView, DArray, DevNull, Diagonal, @@ -95,6 +97,7 @@ export StridedMatrix, StridedVecOrMat, StridedVector, + StridedView, SubArray, SubDArray, SubOrDArray, @@ -577,6 +580,7 @@ export sum, sum_kbn, vcat, + view, vec, zeros, diff --git a/base/sysimg.jl b/base/sysimg.jl index ddb907ae6db82..94e5841fee9e1 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -56,6 +56,8 @@ include("intset.jl") include("dict.jl") include("set.jl") include("iterator.jl") +include("arrayviews.jl") +importall .ArrayViews # compiler import Core.Undef # used internally by compiler diff --git a/test/Makefile b/test/Makefile index bcf1777847225..07fd1175b9515 100644 --- a/test/Makefile +++ b/test/Makefile @@ -3,7 +3,7 @@ include ../Make.inc TESTS = all core keywordargs numbers strings unicode collections hashing \ remote iobuffer arrayops linalg blas fft dsp sparse bitarray random \ - math functional bigint sorting statistics spawn parallel arpack file \ + math arrayviews functional bigint sorting statistics spawn parallel arpack file \ git pkg resolve suitesparse complex version pollfd mpfr broadcast \ socket floatapprox priorityqueue readdlm regex float16 combinatorics \ sysinfo rounding ranges mod2pi euler show diff --git a/test/arrayviews.jl b/test/arrayviews.jl new file mode 100644 index 0000000000000..5e93e20113caa --- /dev/null +++ b/test/arrayviews.jl @@ -0,0 +1,568 @@ +importall Base.ArrayViews +import Base.ArrayViews.ContRank + +#### Testing for Contiguous Views + +avparent = rand(5, 4, 3, 2) + +## 1D +v = contiguous_view(avparent, 10, (20,)) +@test isa(v, ContiguousView{Float64,1}) +@test eltype(v) == eltype(avparent) +@test ndims(v) == 1 +@test length(v) == 20 + +@test size(v) == (20,) +@test [size(v,i) for i=1:2] == [20, 1] +@test strides(v) == (1,) +@test [stride(v,i) for i=1:2] == [1,20] + +@test [v[i] for i=1:20] == avparent[11:30] +@test [v[i,1] for i=1:20] == avparent[11:30] + +## 2D +v = contiguous_view(avparent, 10, (5, 12)) +@test isa(v, ContiguousView{Float64,2}) +@test eltype(v) == eltype(avparent) +@test ndims(v) == 2 +@test length(v) == 60 + +@test size(v) == (5, 12) +@test [size(v,i) for i=1:3] == [5,12,1] +@test strides(v) == (1,5) +@test [stride(v,i) for i=1:3] == [1,5,60] + +@test [v[i] for i=1:60] == avparent[11:70] +@test [v[i,j] for i=1:5, j=1:12] == avparent[:,3:14] +@test [v[i,j,1] for i=1:5, j=1:12] == avparent[:,3:14] + +## 3D +v = contiguous_view(avparent, (5, 4, 6)) +@test isa(v, ContiguousView{Float64,3}) +@test eltype(v) == eltype(avparent) +@test ndims(v) == 3 +@test length(v) == 120 + +@test size(v) == (5, 4, 6) +@test [size(v,i) for i=1:4] == [5,4,6,1] +@test strides(v) == (1,5,20) +@test [stride(v,i) for i=1:4] == [1,5,20,120] + +@test [v[i] for i=1:120] == avparent[1:120] +@test [v[i,j] for i=1:5, j=1:24] == avparent[1:5,1:24] +@test [v[i,j,k] for i=1:5, j=1:4, k=1:6] == avparent[1:5, 1:4, 1:6] +@test [v[i,j,k,1] for i=1:5, j=1:4, k=1:6] == avparent[1:5, 1:4, 1:6] + +## 4D +v = contiguous_view(avparent, 0, (5, 4, 3, 2)) +@test isa(v, ContiguousView{Float64,4}) +@test eltype(v) == eltype(avparent) +@test ndims(v) == 4 +@test length(v) == 120 + +@test size(v) == (5, 4, 3, 2) +@test [size(v,i) for i=1:5] == [5,4,3,2,1] +@test strides(v) == (1,5,20,60) +@test [stride(v,i) for i=1:5] == [1,5,20,60,120] + +@test [v[i] for i=1:120] == avparent[1:120] +@test [v[i,j] for i=1:5, j=1:24] == avparent[1:5, 1:24] +@test [v[i,j,k] for i=1:5, j=1:4, k=1:6] == avparent[1:5, 1:4, 1:6] +@test [v[i,j,k,l] for i=1:5, j=1:4, k=1:3, l=1:2] == avparent[1:5, 1:4, 1:3, 1:2] +@test [v[i,j,k,l,1] for i=1:5, j=1:4, k=1:3, l=1:2] == avparent[1:5, 1:4, 1:3, 1:2] + + +#### Testing for Strided Views + +avparent = reshape(1.:1680., (8, 7, 6, 5)) + +# N=1, M=1 +v = strided_view(avparent, 10, (12,), ContRank{1}, (1,)) +isa(v, StridedView{Float64, 1, 1}) +@test ndims(v) == 1 +@test length(v) == 12 +@test iscontiguous(v) == true +@test contiguousrank(v) == 1 + +@test size(v) == (12,) +@test Int[size(v, i) for i=1:2] == [12, 1] +@test strides(v) == (1,) +@test stride(v,1) == 1 + +@test [v[i] for i=1:12] == avparent[11:22] +@test [v[i,1] for i=1:12] == avparent[11:22] +@test [v[i,1,1] for i=1:12] == avparent[11:22] + +# N=1, M=0 +v = strided_view(avparent, 10, (12,), ContRank{0}, (2,)) +isa(v, StridedView{Float64, 1, 0}) +@test ndims(v) == 1 +@test length(v) == 12 +@test iscontiguous(v) == false +@test contiguousrank(v) == 0 + +@test size(v) == (12,) +@test Int[size(v, i) for i=1:2] == [12, 1] +@test strides(v) == (2,) +@test stride(v,1) == 2 + +@test [v[i] for i=1:12] == avparent[11:2:33] +@test [v[i,1] for i=1:12] == avparent[11:2:33] +@test [v[i,1,1] for i=1:12] == avparent[11:2:33] + +# N=2, M=2 +v = strided_view(avparent, 8, (8, 7), ContRank{2}, (1, 8)) +isa(v, StridedView{Float64, 2, 2}) +@test ndims(v) == 2 +@test length(v) == 56 +@test iscontiguous(v) == true +@test contiguousrank(v) == 2 + +@test size(v) == (8, 7) +@test Int[size(v, i) for i=1:3] == [8, 7, 1] +@test strides(v) == (1, 8) +@test Int[stride(v, i) for i=1:2] == [1, 8] + +@test [v[i,j] for i=1:8, j=1:7] == avparent[1:8, 2:8] +@test [v[i,j,1] for i=1:8, j=1:7] == avparent[1:8, 2:8] +@test [v[i] for i=1:56] == vec(avparent[1:8, 2:8]) + +### N=2, M=1 +v = strided_view(avparent, 8, (6, 7), ContRank{1}, (1, 8)) +isa(v, StridedView{Float64, 2, 1}) +@test ndims(v) == 2 +@test length(v) == 42 +@test iscontiguous(v) == false +@test contiguousrank(v) == 1 + +@test size(v) == (6, 7) +@test Int[size(v, i) for i=1:3] == [6, 7, 1] +@test strides(v) == (1, 8) +@test Int[stride(v, i) for i=1:2] == [1, 8] + +@test [v[i,j] for i=1:6, j=1:7] == avparent[1:6, 2:8] +@test [v[i,j,1] for i=1:6, j=1:7] == avparent[1:6, 2:8] +@test [v[i] for i=1:42] == vec(avparent[1:6, 2:8]) + +### N=2, M=0 +v = strided_view(avparent, 8, (4, 7), ContRank{0}, (2, 8)) +isa(v, StridedView{Float64, 2, 0}) +@test ndims(v) == 2 +@test length(v) == 28 +@test iscontiguous(v) == false +@test contiguousrank(v) == 0 + +@test size(v) == (4, 7) +@test Int[size(v, i) for i=1:3] == [4, 7, 1] +@test strides(v) == (2, 8) +@test Int[stride(v, i) for i=1:2] == [2, 8] + +@test [v[i,j] for i=1:4, j=1:7] == avparent[1:2:7, 2:8] +@test [v[i,j,1] for i=1:4, j=1:7] == avparent[1:2:7, 2:8] +@test [v[i] for i=1:28] == vec(avparent[1:2:7, 2:8]) + +### N=3, M=3 +v = strided_view(avparent, (8, 7, 6), ContRank{3}, (1, 8, 56)) +isa(v, StridedView{Float64, 3, 3}) +@test ndims(v) == 3 +@test length(v) == 336 +@test iscontiguous(v) == true +@test contiguousrank(v) == 3 + +@test size(v) == (8, 7, 6) +@test Int[size(v, i) for i=1:4] == [8, 7, 6, 1] +@test strides(v) == (1, 8, 56) +@test Int[stride(v, i) for i=1:3] == [1, 8, 56] + +vr = avparent[1:8, 1:7, 1:6] +@test [v[i,j,k] for i=1:8, j=1:7, k=1:6] == vr +@test [v[i,j,k,1] for i=1:8, j=1:7, k=1:6] == vr +@test [v[i,j] for i=1:8, j=1:42] == vr[:,:] +@test [v[i] for i=1:336] == vr[:] + +### N=3, M=2 +v = strided_view(avparent, (8, 6, 5), ContRank{2}, (1, 8, 56)) +isa(v, StridedView{Float64, 3, 2}) +@test ndims(v) == 3 +@test length(v) == 240 +@test iscontiguous(v) == false +@test contiguousrank(v) == 2 + +@test size(v) == (8, 6, 5) +@test Int[size(v, i) for i=1:4] == [8, 6, 5, 1] +@test strides(v) == (1, 8, 56) +@test Int[stride(v, i) for i=1:3] == [1, 8, 56] + +vr = avparent[1:8, 1:6, 1:5] +@test [v[i,j,k] for i=1:8, j=1:6, k=1:5] == vr +@test [v[i,j,k,1] for i=1:8, j=1:6, k=1:5] == vr +@test [v[i,j] for i=1:8, j=1:30] == vr[:,:] +@test [v[i] for i=1:240] == vr[:] + +### N=3, M=1 +v = strided_view(avparent, (7, 6, 5), ContRank{1}, (1, 8, 56)) +isa(v, StridedView{Float64, 3, 2}) +@test ndims(v) == 3 +@test length(v) == 210 +@test iscontiguous(v) == false +@test contiguousrank(v) == 1 + +@test size(v) == (7, 6, 5) +@test Int[size(v, i) for i=1:4] == [7, 6, 5, 1] +@test strides(v) == (1, 8, 56) +@test Int[stride(v, i) for i=1:3] == [1, 8, 56] + +vr = avparent[1:7, 1:6, 1:5] +@test [v[i,j,k] for i=1:7, j=1:6, k=1:5] == vr +@test [v[i,j,k,1] for i=1:7, j=1:6, k=1:5] == vr +@test [v[i,j] for i=1:7, j=1:30] == vr[:,:] +@test [v[i] for i=1:210] == vr[:] + +### N=3, M=0 +v = strided_view(avparent, (4, 6, 5), ContRank{0}, (2, 8, 56)) +isa(v, StridedView{Float64, 3, 2}) +@test ndims(v) == 3 +@test length(v) == 120 +@test iscontiguous(v) == false +@test contiguousrank(v) == 0 + +@test size(v) == (4, 6, 5) +@test Int[size(v, i) for i=1:4] == [4, 6, 5, 1] +@test strides(v) == (2, 8, 56) +@test Int[stride(v, i) for i=1:3] == [2, 8, 56] + +vr = avparent[1:2:7, 1:6, 1:5] +@test [v[i,j,k] for i=1:4, j=1:6, k=1:5] == vr +@test [v[i,j,k,1] for i=1:4, j=1:6, k=1:5] == vr +@test [v[i,j] for i=1:4, j=1:30] == vr[:,:] +@test [v[i] for i=1:120] == vr[:] + +### N=4, M=2 +v = strided_view(avparent, (8, 6, 4, 3), ContRank{2}, (1, 8, 56, 336)) +isa(v, StridedView{Float64, 4, 2}) +@test ndims(v) == 4 +@test length(v) == 576 +@test iscontiguous(v) == false +@test contiguousrank(v) == 2 + +@test size(v) == (8, 6, 4, 3) +@test Int[size(v,i) for i=1:5] == [8, 6, 4, 3, 1] +@test strides(v) == (1, 8, 56, 336) +@test Int[stride(v,i) for i=1:4] == [1, 8, 56, 336] + +vr = avparent[1:8, 1:6, 1:4, 1:3] +@test [v[i,j,k,l] for i=1:8, j=1:6, k=1:4, l=1:3] == vr +@test [v[i,j,k,l,1] for i=1:8, j=1:6, k=1:4, l=1:3] == vr +@test [v[i,j,k] for i=1:8, j=1:6, k=1:12] == vr[:,:,:] +@test [v[i,j] for i=1:8, j=1:72] == vr[:,:] +@test [v[i] for i=1:576] == vr[:] + +### N=4, M=0 +v = strided_view(avparent, (4, 6, 4, 3), ContRank{0}, (2, 8, 56, 336)) +isa(v, StridedView{Float64, 4, 2}) +@test ndims(v) == 4 +@test length(v) == 288 +@test iscontiguous(v) == false +@test contiguousrank(v) == 0 + +@test size(v) == (4, 6, 4, 3) +@test Int[size(v,i) for i=1:5] == [4, 6, 4, 3, 1] +@test strides(v) == (2, 8, 56, 336) +@test Int[stride(v,i) for i=1:4] == [2, 8, 56, 336] + +vr = avparent[1:2:7, 1:6, 1:4, 1:3] +@test [v[i,j,k,l] for i=1:4,j=1:6,k=1:4,l=1:3] == vr +@test [v[i,j,k,l,1] for i=1:4,j=1:6,k=1:4,l=1:3] == vr +@test [v[i,j,k] for i=1:4,j=1:6,k=1:12] == vr[:,:,:] +@test [v[i,j] for i=1:4,j=1:72] == vr[:,:] +@test [v[i] for i=1:288] == vr[:] + + +#### Test Subviews + +## tools to facilitate array view testing +function _test_arrview(a, r, subs...) + v = view(a, subs...) + + siz_r = size(r) + siz_v = size(v) + + if siz_r != siz_v + error("Incorrect size: get $(siz_v), but expect $(siz_r)") + end + + for i = 1 : length(v) + if v[i] != r[i] + println("v = ") + println(v) + println("r = ") + println(r) + error("Incorrect content.") + end + end +end + +macro test_arrview(a_, subs...) + esc(:(_test_arrview($a_, ($a_)[$(subs...)], $(subs...)))) +end + +#### test views from arrays + +avparent = reshape(1.:1680., (8, 7, 6, 5)) + +# 1D +@test_arrview(avparent, 3) +@test_arrview(avparent, :) +@test_arrview(avparent, 1:12) +@test_arrview(avparent, 3:2:36) + +# 2D +@test_arrview(avparent, 4, 2) +@test_arrview(avparent, 4, :) +@test_arrview(avparent, 4, 3:10) +@test_arrview(avparent, 4, 2:2:10) + +@test_arrview(avparent, :, 2) +@test_arrview(avparent, :, :) +@test_arrview(avparent, :, 3:10) +@test_arrview(avparent, :, 2:2:10) + +@test_arrview(avparent, 1:6, 2) +@test_arrview(avparent, 1:6, :) +@test_arrview(avparent, 1:6, 3:10) +@test_arrview(avparent, 1:6, 2:2:10) + +@test_arrview(avparent, 1:2:8, 2) +@test_arrview(avparent, 1:2:8, :) +@test_arrview(avparent, 1:2:8, 3:10) +@test_arrview(avparent, 1:2:8, 2:2:10) + +# 3D +@test_arrview(avparent, 4, 3, 2) +@test_arrview(avparent, 4, 3, :) +@test_arrview(avparent, 4, 3, 2:5) +@test_arrview(avparent, 4, 3, 1:2:6) + +@test_arrview(avparent, 4, :, 2) +@test_arrview(avparent, 4, :, :) +@test_arrview(avparent, 4, :, 2:5) +@test_arrview(avparent, 4, :, 1:2:6) + +@test_arrview(avparent, 4, 3:7, 2) +@test_arrview(avparent, 4, 3:7, :) +@test_arrview(avparent, 4, 3:7, 2:5) +@test_arrview(avparent, 4, 3:7, 1:2:6) + +@test_arrview(avparent, 4, 1:2:5, 2) +@test_arrview(avparent, 4, 1:2:5, :) +@test_arrview(avparent, 4, 1:2:5, 2:5) +@test_arrview(avparent, 4, 1:2:5, 1:2:6) + +@test_arrview(avparent, :, 3, 2) +@test_arrview(avparent, :, 3, :) +@test_arrview(avparent, :, 3, 2:5) +@test_arrview(avparent, :, 3, 1:2:6) + +@test_arrview(avparent, :, :, 2) +@test_arrview(avparent, :, :, :) +@test_arrview(avparent, :, :, 2:5) +@test_arrview(avparent, :, :, 1:2:6) + +@test_arrview(avparent, :, 3:7, 2) +@test_arrview(avparent, :, 3:7, :) +@test_arrview(avparent, :, 3:7, 2:5) +@test_arrview(avparent, :, 3:7, 1:2:6) + +@test_arrview(avparent, :, 1:2:5, 2) +@test_arrview(avparent, :, 1:2:5, :) +@test_arrview(avparent, :, 1:2:5, 2:5) +@test_arrview(avparent, :, 1:2:5, 1:2:6) + +@test_arrview(avparent, 2:7, 3, 2) +@test_arrview(avparent, 2:7, 3, :) +@test_arrview(avparent, 2:7, 3, 2:5) +@test_arrview(avparent, 2:7, 3, 1:2:6) + +@test_arrview(avparent, 2:7, :, 2) +@test_arrview(avparent, 2:7, :, :) +@test_arrview(avparent, 2:7, :, 2:5) +@test_arrview(avparent, 2:7, :, 1:2:6) + +@test_arrview(avparent, 2:7, 3:7, 2) +@test_arrview(avparent, 2:7, 3:7, :) +@test_arrview(avparent, 2:7, 3:7, 2:5) +@test_arrview(avparent, 2:7, 3:7, 1:2:6) + +@test_arrview(avparent, 2:7, 1:2:5, 2) +@test_arrview(avparent, 2:7, 1:2:5, :) +@test_arrview(avparent, 2:7, 1:2:5, 2:5) +@test_arrview(avparent, 2:7, 1:2:5, 1:2:6) + +@test_arrview(avparent, 1:2:7, 3, 2) +@test_arrview(avparent, 1:2:7, 3, :) +@test_arrview(avparent, 1:2:7, 3, 2:5) +@test_arrview(avparent, 1:2:7, 3, 1:2:6) + +@test_arrview(avparent, 1:2:7, :, 2) +@test_arrview(avparent, 1:2:7, :, :) +@test_arrview(avparent, 1:2:7, :, 2:5) +@test_arrview(avparent, 1:2:7, :, 1:2:6) + +@test_arrview(avparent, 1:2:7, 3:7, 2) +@test_arrview(avparent, 1:2:7, 3:7, :) +@test_arrview(avparent, 1:2:7, 3:7, 2:5) +@test_arrview(avparent, 1:2:7, 3:7, 1:2:6) + +@test_arrview(avparent, 1:2:7, 1:2:5, 2) +@test_arrview(avparent, 1:2:7, 1:2:5, :) +@test_arrview(avparent, 1:2:7, 1:2:5, 2:5) +@test_arrview(avparent, 1:2:7, 1:2:5, 1:2:6) + +# Some 4D Tests +@test_arrview(avparent, 4, :, 3, 4) +@test_arrview(avparent, 4, :, :, 4) +@test_arrview(avparent, 4, :, 3:5, 4) +@test_arrview(avparent, 4, :, 1:2:5, 4) + +@test_arrview(avparent, :, :, 3, 4) +@test_arrview(avparent, :, :, :, 4) +@test_arrview(avparent, :, :, 3:5, 4) +@test_arrview(avparent, :, :, 1:2:5, 4) + +@test_arrview(avparent, 2:7, :, 3, 4) +@test_arrview(avparent, 2:7, :, :, 4) +@test_arrview(avparent, 2:7, :, 3:5, 4) +@test_arrview(avparent, 2:7, :, 1:2:5, 4) + +@test_arrview(avparent, 1:2:7, :, 3, 4) +@test_arrview(avparent, 1:2:7, :, :, 4) +@test_arrview(avparent, 1:2:7, :, 3:5, 4) +@test_arrview(avparent, 1:2:7, :, 1:2:5, 4) + +@test_arrview(avparent, 4, :, 3, :) +@test_arrview(avparent, 4, :, :, :) +@test_arrview(avparent, 4, :, 3:5, :) +@test_arrview(avparent, 4, :, 1:2:5, :) + +@test_arrview(avparent, :, :, 3, :) +@test_arrview(avparent, :, :, :, :) +@test_arrview(avparent, :, :, 3:5, :) +@test_arrview(avparent, :, :, 1:2:5, :) + +@test_arrview(avparent, 2:7, :, 3, :) +@test_arrview(avparent, 2:7, :, :, :) +@test_arrview(avparent, 2:7, :, 3:5, :) +@test_arrview(avparent, 2:7, :, 1:2:5, :) + +@test_arrview(avparent, 1:2:7, :, 3, :) +@test_arrview(avparent, 1:2:7, :, :, :) +@test_arrview(avparent, 1:2:7, :, 3:5, :) +@test_arrview(avparent, 1:2:7, :, 1:2:5, :) + +@test_arrview(avparent, 4, :, 3, 2:5) +@test_arrview(avparent, 4, :, :, 2:5) +@test_arrview(avparent, 4, :, 3:5, 2:5) +@test_arrview(avparent, 4, :, 1:2:5, 2:5) + +@test_arrview(avparent, :, :, 3, 2:5) +@test_arrview(avparent, :, :, :, 2:5) +@test_arrview(avparent, :, :, 3:5, 2:5) +@test_arrview(avparent, :, :, 1:2:5, 2:5) + +@test_arrview(avparent, 2:7, :, 3, 2:5) +@test_arrview(avparent, 2:7, :, :, 2:5) +@test_arrview(avparent, 2:7, :, 3:5, 2:5) +@test_arrview(avparent, 2:7, :, 1:2:5, 2:5) + +@test_arrview(avparent, 1:2:7, :, 3, 2:5) +@test_arrview(avparent, 1:2:7, :, :, 2:5) +@test_arrview(avparent, 1:2:7, :, 3:5, 2:5) +@test_arrview(avparent, 1:2:7, :, 1:2:5, 2:5) + + +#### Test Subviews of Views + +function print_subscripts(subs1, subs2) + println("Error happens on: ") + + subs1s = join([repr(i) for i in subs1], ", ") + subs2s = join([repr(i) for i in subs2], ", ") + + println(" subs1 = [$subs1s]") + println(" subs2 = [$subs2s]") +end + +function test_arrview2(a, subs1, subs2) + v = view(a, subs1...) + v2 = view(v, subs2...) + v2r = view(copy(v), subs2...) + + siz_v = size(v2) + siz_r = size(v2r) + + if siz_v != siz_r + print_subscripts(subs1, subs2) + error("Incorrect size: get $(siz_v), but expect $(siz_r)") + end + + for i = 1 : length(v2) + if v2[i] != v2r[i] + print_subscripts(subs1, subs2) + println("v = ") + println(v2) + println("r = ") + println(v2r) + error("Incorrect content.") + end + end +end + +avparent = reshape(1:6912, (12, 12, 8, 6)) + +# 1D --> 1D +for sa in {(:), 1:36, 2:2:36} + v1 = view(avparent, sa) + for sb in {4, (:), 1:length(v1), 3:2:length(v1)} + test_arrview2(avparent, (sa,), (sb,)) + end +end + +# 2D --> 2D +for sa1 in {(:), 1:10, 2:2:12}, sa2 = {(:), 1:12, 2:2:16} + v1 = view(avparent, sa1, sa2) + for sb1 in {4, (:), 2:size(v1,1), 2:2:size(v1,1)}, + sb2 in {4, (:), 2:size(v1,2), 2:2:size(v1,2)} + test_arrview2(avparent, (sa1, sa2), (sb1, sb2)) + end +end + +# 3D --> 3D +for sa1 in {(:), 1:10, 2:2:12}, + sa2 in {(:), 1:10, 2:2:12}, + sa3 in {(:), 1:7, 2:2:8} + v1 = view(avparent, sa1, sa2, sa3) + (d1, d2, d3) = size(v1) + for sb1 in {(:), 2:d1, 2:2:d1}, + sb2 in {(:), 2:d2, 2:2:d2}, + sb3 in {(:), 2:d3, 2:2:d3} + test_arrview2(avparent, (sa1, sa2, sa3), (sb1, sb2, sb3)) + end +end + +# Test Linear Algebra on views + +avparent = rand(7, 8) +bvparent = rand(8, 5) + +_ab = avparent * bvparent + +@test _ab == view(avparent, :, :) * bvparent +@test _ab == avparent * view(bvparent, :, :) +@test _ab == view(avparent, :, :) * view(bvparent, :, :) + +for j = 1:size(bvparent,2) + @test_approx_eq _ab[:,j] view(avparent,:,:) * view(bvparent,:,j) +end + +@test avparent[:, 2:2:7] * bvparent[1:3, 1:2:5] == view(avparent, :, 2:2:7) * view(bvparent, 1:3, 1:2:5) + diff --git a/test/runtests.jl b/test/runtests.jl index e788ff52b5fd3..93f4a22072c0c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,7 +1,7 @@ testnames = [ "core", "keywordargs", "numbers", "strings", "collections", "hashing", "remote", "iobuffer", "arrayops", "linalg", - "blas", "fft", "dsp", "sparse", "bitarray", "random", "math", + "blas", "fft", "dsp", "sparse", "bitarray", "random", "math", "arrayviews", "functional", "bigint", "sorting", "statistics", "spawn", "parallel", "priorityqueue", "arpack", "file", "suitesparse", "version", "resolve", "pollfd", "mpfr", "broadcast", "complex", "socket",