diff --git a/base/abstractarray.jl b/base/abstractarray.jl index fec3620e2a2bb..b8feeffa4d745 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -5,9 +5,15 @@ typealias AbstractVector{T} AbstractArray{T,1} typealias AbstractMatrix{T} AbstractArray{T,2} typealias AbstractVecOrMat{T} Union{AbstractVector{T}, AbstractMatrix{T}} -typealias RangeIndex Union{Int, Range{Int}, UnitRange{Int}, Colon} -typealias UnitRangeInteger{T<:Integer} UnitRange{T} -typealias Indices{N} NTuple{N,UnitRangeInteger} +typealias RangeIndex Union{Int, Range{Int}, AbstractUnitRange{Int}, Colon} +typealias Indices{N} NTuple{N,AbstractUnitRange} +typealias IndicesOne{N} NTuple{N,OneTo} +typealias DimOrInd Union{Integer, AbstractUnitRange} +typealias DimsOrInds{N} NTuple{N,DimOrInd} + +macro _inline_pure_meta() + Expr(:meta, :inline, :pure) +end ## Basic functions ## @@ -28,27 +34,20 @@ size{N}(x, d1::Integer, d2::Integer, dx::Vararg{Integer, N}) = (size(x, d1), siz Returns the valid range of indices for array `A` along dimension `d`. """ -function indices(A::AbstractArray, d) - @_inline_meta - 1:size(A,d) -end +indices{T,N}(A::AbstractArray{T,N}, d) = d <= N ? indices(A)[d] : OneTo(1) """ indices(A) Returns the tuple of valid indices for array `A`. """ -indices{T,N}(A::AbstractArray{T,N}) = _indices((), A) -_indices{T,N}(out::NTuple{N}, A::AbstractArray{T,N}) = out -function _indices(out, A::AbstractArray) - @_inline_meta - _indices((out..., indices(A, length(out)+1)), A) -end -# This simpler implementation suffers from #16327 -# function indices{T,N}(A::AbstractArray{T,N}) -# @_inline_meta -# ntuple(d->indices(A, d), Val{N}) -# end -indices1(A) = (@_inline_meta; indices(A, 1)) +function indices{T,N}(A::AbstractArray{T,N}) + @_inline_pure_meta + map(s->OneTo(s), size(A)) +end + +indices1{T}(A::AbstractArray{T,0}) = OneTo(1) +indices1{T}(A::AbstractArray{T}) = indices(A)[1] + """ linearindices(A) @@ -144,45 +143,6 @@ linearindexing(A::AbstractArray, B::AbstractArray...) = linearindexing(linearind linearindexing(::LinearFast, ::LinearFast) = LinearFast() linearindexing(::LinearIndexing, ::LinearIndexing) = LinearSlow() -abstract IndicesBehavior -immutable IndicesStartAt1 <: IndicesBehavior end # indices 1:size(A,d) -immutable IndicesUnitRange <: IndicesBehavior end # arb UnitRange indices -immutable IndicesList <: IndicesBehavior end # indices like (:cat, :dog, :mouse) - -indicesbehavior(A::AbstractArray) = indicesbehavior(typeof(A)) -indicesbehavior{T<:AbstractArray}(::Type{T}) = IndicesStartAt1() -indicesbehavior(::Number) = IndicesStartAt1() - -abstract IndicesPerformance -immutable IndicesFast1D <: IndicesPerformance end # indices(A, d) is fast -immutable IndicesSlow1D <: IndicesPerformance end # indices(A) is better than indices(A,d) - -indicesperformance(A::AbstractArray) = indicesperformance(typeof(A)) -indicesperformance{T<:AbstractArray}(::Type{T}) = IndicesFast1D() - -""" - shape(A) - -Returns a tuple specifying the "shape" of array `A`. For arrays with -conventional indexing (indices start at 1), this is equivalent to -`size(A)`; otherwise it is equivalent to `indices(A)`. -""" -shape(a) = shape(indicesbehavior(a), a) -""" - shape(A, d) - -Specifies the "shape" of the array `A` along dimension `d`. For arrays -with conventional indexing (starting at 1), this is equivalent to -`size(A, d)`; for arrays with unconventional indexing (indexing may -start at something different from 1), it is equivalent to `indices(A, -d)`. -""" -shape(a, d) = shape(indicesbehavior(a), a, d) -shape(::IndicesStartAt1, a) = size(a) -shape(::IndicesStartAt1, a, d) = size(a, d) -shape(::IndicesBehavior, a) = indices(a) -shape(::IndicesBehavior, a, d) = indices(a, d) - ## Bounds checking ## @generated function trailingsize{T,N,n}(A::AbstractArray{T,N}, ::Type{Val{n}}) (isa(n, Int) && isa(N, Int)) || error("Must have concrete type") @@ -203,15 +163,15 @@ Return `true` if the given `index` is within the bounds of arrays can extend this method in order to provide a specialized bounds checking implementation. """ -checkindex(::Type{Bool}, inds::UnitRange, i) = throw(ArgumentError("unable to check bounds for indices of type $(typeof(i))")) -checkindex(::Type{Bool}, inds::UnitRange, i::Real) = (first(inds) <= i) & (i <= last(inds)) -checkindex(::Type{Bool}, inds::UnitRange, ::Colon) = true -function checkindex(::Type{Bool}, inds::UnitRange, r::Range) +checkindex(::Type{Bool}, inds::AbstractUnitRange, i) = throw(ArgumentError("unable to check bounds for indices of type $(typeof(i))")) +checkindex(::Type{Bool}, inds::AbstractUnitRange, i::Real) = (first(inds) <= i) & (i <= last(inds)) +checkindex(::Type{Bool}, inds::AbstractUnitRange, ::Colon) = true +function checkindex(::Type{Bool}, inds::AbstractUnitRange, r::Range) @_propagate_inbounds_meta isempty(r) | (checkindex(Bool, inds, first(r)) & checkindex(Bool, inds, last(r))) end -checkindex{N}(::Type{Bool}, indx::UnitRange, I::AbstractArray{Bool,N}) = N == 1 && indx == indices1(I) -function checkindex(::Type{Bool}, inds::UnitRange, I::AbstractArray) +checkindex{N}(::Type{Bool}, indx::AbstractUnitRange, I::AbstractArray{Bool,N}) = N == 1 && indx == indices1(I) +function checkindex(::Type{Bool}, inds::AbstractUnitRange, I::AbstractArray) @_inline_meta b = true for i in I @@ -221,7 +181,8 @@ function checkindex(::Type{Bool}, inds::UnitRange, I::AbstractArray) end # check all indices/dimensions -# To make extension easier, avoid specializing checkbounds on index types +# To make extension easier, avoid specializations of checkbounds on index types +# (That said, typically one does not need to specialize this function.) """ checkbounds(Bool, array, indexes...) @@ -231,15 +192,12 @@ behaviors. """ function checkbounds(::Type{Bool}, A::AbstractArray, I...) @_inline_meta - _checkbounds(Bool, indicesperformance(A), A, I...) -end -function _checkbounds(::Type{Bool}, ::IndicesSlow1D, A::AbstractArray, I...) - @_inline_meta - checkbounds_indices(indices(A), I) + _chkbounds(A, I...) end -_checkbounds(::Type{Bool}, ::IndicesSlow1D, A::AbstractArray, I::AbstractArray{Bool}) = _chkbnds(A, (true,), I) +_chkbounds(A::AbstractArray, i::Integer) = (@_inline_meta; checkindex(Bool, linearindices(A), i)) +_chkbounds(A::AbstractArray, I::AbstractArray{Bool}) = (@_inline_meta; checkbounds_logical(A, I)) +_chkbounds(A::AbstractArray, I...) = (@_inline_meta; checkbounds_indices(indices(A), I)) -# Bounds-checking for arrays for which indices(A) is faster than indices(A, d) checkbounds_indices(::Tuple{}, ::Tuple{}) = true checkbounds_indices(::Tuple{}, I::Tuple{Any}) = (@_inline_meta; checkindex(Bool, 1:1, I[1])) checkbounds_indices(::Tuple{}, I::Tuple) = (@_inline_meta; checkindex(Bool, 1:1, I[1]) & checkbounds_indices((), tail(I))) @@ -247,52 +205,11 @@ checkbounds_indices(inds::Tuple{Any}, I::Tuple{Any}) = (@_inline_meta; checkinde checkbounds_indices(inds::Tuple, I::Tuple{Any}) = (@_inline_meta; checkindex(Bool, 1:prod(map(dimlength, inds)), I[1])) checkbounds_indices(inds::Tuple, I::Tuple) = (@_inline_meta; checkindex(Bool, inds[1], I[1]) & checkbounds_indices(tail(inds), tail(I))) -# Bounds-checking for arrays for which indices(A, d) is fast -function _checkbounds(::Type{Bool}, ::IndicesFast1D, A::AbstractArray, I...) - @_inline_meta - # checked::NTuple{M} means we have checked dimensions 1:M-1, now - # need to check dimension M. checked[M] indicates whether all the - # previous ones are in-bounds. - # By growing checked, it allows us to test whether we've processed - # the same number of dimensions as the array, even while - # supporting CartesianIndex - _chkbnds(A, (true,), I...) -end -checkbounds(::Type{Bool}, A::AbstractArray) = checkbounds(Bool, A, 1) # 0-d case # Single logical array indexing: -_chkbnds(A::AbstractArray, ::NTuple{1,Bool}, I::AbstractArray{Bool}) = indices(A) == indices(I) -_chkbnds(A::AbstractArray, ::NTuple{1,Bool}, I::AbstractVector{Bool}) = length(A) == length(I) -_chkbnds(A::AbstractVector, ::NTuple{1,Bool}, I::AbstractArray{Bool}) = length(A) == length(I) -_chkbnds(A::AbstractVector, ::NTuple{1,Bool}, I::AbstractVector{Bool}) = indices(A) == indices(I) -# Linear indexing: -function _chkbnds(A::AbstractVector, ::NTuple{1,Bool}, I) - @_inline_meta - checkindex(Bool, indices1(A), I) -end -function _chkbnds(A::AbstractArray, ::NTuple{1,Bool}, I) - @_inline_meta - checkindex(Bool, 1:length(A), I) -end -# When all indices have been checked: -_chkbnds{M}(A, checked::NTuple{M,Bool}) = checked[M] -# When the number of indices matches the array dimensionality: -function _chkbnds{T,N}(A::AbstractArray{T,N}, checked::NTuple{N,Bool}, I1) - @_inline_meta - checked[N] & checkindex(Bool, indices(A, N), I1) -end -# When the last checked dimension is not equal to the array dimensionality: -# TODO: for #14770 (deprecating partial linear indexing), change to 1:trailingsize(...) to 1:1 -function _chkbnds{T,N,M}(A::AbstractArray{T,N}, checked::NTuple{M,Bool}, I1) - @_inline_meta - checked[M] & checkindex(Bool, 1:trailingsize(A, Val{M}), I1) -end -# Checking an interior dimension: -function _chkbnds{T,N,M}(A::AbstractArray{T,N}, checked::NTuple{M,Bool}, I1, I...) - @_inline_meta - # grow checked by one - newchecked = (checked..., checked[M] & checkindex(Bool, indices(A, M), I1)) - _chkbnds(A, newchecked, I...) -end +checkbounds_logical(A::AbstractArray, I::AbstractArray{Bool}) = indices(A) == indices(I) +checkbounds_logical(A::AbstractArray, I::AbstractVector{Bool}) = length(A) == length(I) +checkbounds_logical(A::AbstractVector, I::AbstractArray{Bool}) = length(A) == length(I) +checkbounds_logical(A::AbstractVector, I::AbstractVector{Bool}) = indices(A) == indices(I) throw_boundserror(A, I) = (@_noinline_meta; throw(BoundsError(A, I))) @@ -312,7 +229,6 @@ checkbounds(A::AbstractArray) = checkbounds(A, 1) # 0-d case ## Constructors ## # default arguments to similar() -typealias SimIdx Union{Integer,UnitRangeInteger} """ similar(array, [element_type=eltype(array)], [dims=size(array)]) @@ -348,79 +264,54 @@ different element type it will create a regular `Array` instead: 2.18425e-314 2.18425e-314 2.18425e-314 2.18425e-314 2.18425e-314 2.18425e-314 2.18425e-314 2.18425e-314 -See also `allocate_for`. """ similar{T}(a::AbstractArray{T}) = similar(a, T) -similar( a::AbstractArray, T::Type) = _similar(indicesbehavior(a), a, T) -similar{T}(a::AbstractArray{T}, dims::Tuple) = similar(a, T, dims) -similar{T}(a::AbstractArray{T}, dims::SimIdx...) = similar(a, T, dims) -similar( a::AbstractArray, T::Type, dims::SimIdx...) = similar(a, T, dims) -similar( a::AbstractArray, T::Type, dims::DimsInteger) = similar(a, T, convert(Dims, dims)) +similar( a::AbstractArray, T::Type) = similar(a, T, to_shape(indices(a))) +similar{T}(a::AbstractArray{T}, dims::Tuple) = similar(a, T, to_shape(dims)) +similar{T}(a::AbstractArray{T}, dims::DimOrInd...) = similar(a, T, to_shape(dims)) +similar( a::AbstractArray, T::Type, dims::DimOrInd...) = similar(a, T, to_shape(dims)) +similar( a::AbstractArray, T::Type, dims) = similar(a, T, to_shape(dims)) # similar creates an Array by default -similar( a::AbstractArray, T::Type, dims::Dims) = Array(T, dims) +similar( a::AbstractArray, T::Type, dims::Dims) = Array{T}(dims) -_similar(::IndicesStartAt1, a::AbstractArray, T::Type) = similar(a, T, size(a)) -_similar(::IndicesBehavior, a::AbstractArray, T::Type) = similar(a, T, indices(a)) +to_shape(::Tuple{}) = () +to_shape(dims::Dims) = dims +to_shape(dims::DimsOrInds) = map(to_shape, dims) +# each dimension +to_shape(i::Int) = i +to_shape(i::Integer) = Int(i) +to_shape(r::OneTo) = Int(last(r)) +to_shape(r::UnitRange) = convert(UnitRange{Int}, r) """ - allocate_for(storagetype, referencearray, [shape]) + similar(storagetype, indices) Create an uninitialized mutable array analogous to that specified by -`storagetype`, but with type and shape specified by the final two -arguments. The main purpose of this function is to support allocation -of arrays that may have unconventional indexing (starting at other -than 1), as determined by `referencearray` and the optional `shape` -information. +`storagetype`, but with `indices` specified by the last +argument. `storagetype` might be a type or a function. **Examples**: - allocate_for(Array{Int}, A) + similar(Array{Int}, indices(A)) creates an array that "acts like" an `Array{Int}` (and might indeed be backed by one), but which is indexed identically to `A`. If `A` has -conventional indexing, this will likely just call +conventional indexing, this will be identical to `Array{Int}(size(A))`, but if `A` has unconventional indexing then the indices of the result will match `A`. - allocate_for(BitArray, A, (shape(A, 2),)) + similar(BitArray, (indices(A, 2),)) would create a 1-dimensional logical array whose indices match those of the columns of `A`. -The main purpose of the `referencearray` argument is to select a -particular array type supporting unconventional indexing (as it is -possible that several different ones will be simultaneously in use). + similar(dims->zeros(Int, dims), indices(A)) -See also `similar`. +would create an array of `Int`, initialized to zero, matching the +indices of `A`. """ -allocate_for(f, a, shape::Union{SimIdx,Tuple{Vararg{SimIdx}}}) = f(shape) -allocate_for(f, a) = allocate_for(f, a, shape(a)) -# allocate_for when passed multiple arrays. Necessary for broadcast, etc. -function allocate_for(f, as::Tuple, shape::Union{SimIdx,Tuple{Vararg{SimIdx}}}) - @_inline_meta - a = promote_indices(as...) - allocate_for(f, a, shape) -end - -promote_indices(a) = a -function promote_indices(a, b, c...) - @_inline_meta - promote_indices(promote_indices(a, b), c...) -end -# overload this to return true for your type, e.g., -# promote_indices(a::OffsetArray, b::OffsetArray) = a -promote_indices(a::AbstractArray, b::AbstractArray) = _promote_indices(indicesbehavior(a), indicesbehavior(b), a, b) -_promote_indices(::IndicesStartAt1, ::IndicesStartAt1, a, b) = a -_promote_indices(::IndicesBehavior, ::IndicesBehavior, a, b) = throw(ArgumentError("types $(typeof(a)) and $(typeof(b)) do not have promote_indices defined")) -promote_indices(a::Number, b::AbstractArray) = b -promote_indices(a::AbstractArray, b::Number) = a - -# Strip off the index-changing container---this assumes that `parent` -# performs such an operation. TODO: since few things in Base need this, it -# would be great to find a way to eliminate this function. -normalize_indices(A) = normalize_indices(indicesbehavior(A), A) -normalize_indices(::IndicesStartAt1, A) = A -normalize_indices(::IndicesBehavior, A) = parent(A) +similar(f, shape::Tuple) = f(to_shape(shape)) +similar(f, dims::DimOrInd...) = similar(f, dims) ## from general iterable to any array @@ -629,6 +520,7 @@ next(A::AbstractArray,i) = (@_propagate_inbounds_meta; (idx, s) = next(i[1], i[2 done(A::AbstractArray,i) = (@_propagate_inbounds_meta; done(i[1], i[2])) # eachindex iterates over all indices. LinearSlow definitions are later. +eachindex(A::AbstractVector) = (@_inline_meta(); indices1(A)) eachindex(A::AbstractArray) = (@_inline_meta(); eachindex(linearindexing(A), A)) function eachindex(A::AbstractArray, B::AbstractArray) @@ -1255,38 +1147,27 @@ end # sub2ind and ind2sub # fallbacks function sub2ind(A::AbstractArray, I...) - @_inline_meta - sub2ind(indicesbehavior(A), A, I...) -end -function sub2ind(::IndicesStartAt1, A::AbstractArray, I...) - @_inline_meta - sub2ind(size(A), I...) -end -function sub2ind(::IndicesBehavior, A::AbstractArray, I...) @_inline_meta sub2ind(indices(A), I...) end function ind2sub(A::AbstractArray, ind) - @_inline_meta - ind2sub(indicesbehavior(A), A, ind) -end -function ind2sub(::IndicesStartAt1, A::AbstractArray, ind) - @_inline_meta - ind2sub(size(A), ind) -end -function ind2sub(::IndicesBehavior, A::AbstractArray, ind) @_inline_meta ind2sub(indices(A), ind) end +# 0-dimensional arrays and indexing with [] sub2ind(::Tuple{}) = 1 sub2ind(::DimsInteger) = 1 sub2ind(::Indices) = 1 sub2ind(::Tuple{}, I::Integer...) = (@_inline_meta; _sub2ind((), 1, 1, I...)) +# Generic cases sub2ind(dims::DimsInteger, I::Integer...) = (@_inline_meta; _sub2ind(dims, 1, 1, I...)) sub2ind(inds::Indices, I::Integer...) = (@_inline_meta; _sub2ind(inds, 1, 1, I...)) -# In 1d, there's a question of whether we're doing cartesian indexing or linear indexing. Support only the former. +# In 1d, there's a question of whether we're doing cartesian indexing +# or linear indexing. Support only the former. sub2ind(inds::Indices{1}, I::Integer...) = throw(ArgumentError("Linear indexing is not defined for one-dimensional arrays")) +sub2ind(inds::Tuple{OneTo}, I::Integer...) = (@_inline_meta; _sub2ind(inds, 1, 1, I...)) # only OneTo is safe +sub2ind(inds::Tuple{OneTo}, i::Integer) = i _sub2ind(::Any, L, ind) = ind function _sub2ind(::Tuple{}, L, ind, i::Integer, I::Integer...) @@ -1300,37 +1181,34 @@ function _sub2ind(inds, L, ind, i::Integer, I::Integer...) end nextL(L, l::Integer) = L*l -nextL(L, r::UnitRange) = L*unsafe_length(r) +nextL(L, r::AbstractUnitRange) = L*unsafe_length(r) offsetin(i, l::Integer) = i-1 -offsetin(i, r::UnitRange) = i-first(r) +offsetin(i, r::AbstractUnitRange) = i-first(r) unsafe_length(r::UnitRange) = r.stop-r.start+1 +unsafe_length(r::OneTo) = length(r) ind2sub(::Tuple{}, ind::Integer) = (@_inline_meta; ind == 1 ? () : throw(BoundsError())) -ind2sub(dims::DimsInteger, ind::Integer) = (@_inline_meta; _ind2sub((), dims, ind-1)) -ind2sub(inds::Indices, ind::Integer) = (@_inline_meta; _ind2sub((), inds, ind-1)) +ind2sub(dims::DimsInteger, ind::Integer) = (@_inline_meta; _ind2sub(dims, ind-1)) +ind2sub(inds::Indices, ind::Integer) = (@_inline_meta; _ind2sub(inds, ind-1)) ind2sub(inds::Indices{1}, ind::Integer) = throw(ArgumentError("Linear indexing is not defined for one-dimensional arrays")) +ind2sub(inds::Tuple{OneTo}, ind::Integer) = (ind,) -_ind2sub(::Tuple{}, ::Tuple{}, ind) = (ind+1,) -function _ind2sub(out, indslast::NTuple{1}, ind) +_ind2sub(::Tuple{}, ind) = (ind+1,) +function _ind2sub(indslast::NTuple{1}, ind) @_inline_meta - (out..., _lookup(ind, indslast[1])) + (_lookup(ind, indslast[1]),) end -function _ind2sub(out, inds, ind) +function _ind2sub(inds, ind) @_inline_meta r1 = inds[1] indnext, f, l = _div(ind, r1) - _ind2sub((out..., ind-l*indnext+f), tail(inds), indnext) + (ind-l*indnext+f, _ind2sub(tail(inds), indnext)...) end _lookup(ind, d::Integer) = ind+1 -_lookup(ind, r::UnitRange) = ind+first(r) +_lookup(ind, r::AbstractUnitRange) = ind+first(r) _div(ind, d::Integer) = div(ind, d), 1, d -_div(ind, r::UnitRange) = (d = unsafe_length(r); (div(ind, d), first(r), d)) - -smart_ind2sub(shape::NTuple{1}, ind) = (ind,) -smart_ind2sub(shape, ind) = ind2sub(shape, ind) -smart_sub2ind(shape::NTuple{1}, i) = (i,) -smart_sub2ind(shape, I...) = (@_inline_meta; sub2ind(shape, I...)) +_div(ind, r::AbstractUnitRange) = (d = unsafe_length(r); (div(ind, d), first(r), d)) # Vectorized forms function sub2ind{N,T<:Integer}(inds::Union{Dims{N},Indices{N}}, I::AbstractVector{T}...) @@ -1405,7 +1283,7 @@ function mapslices(f, A::AbstractArray, dims::AbstractVector) return map(f,A) end - dimsA = [shape(A)...] + dimsA = [indices(A)...] ndimsA = ndims(A) alldims = [1:ndimsA;] @@ -1430,7 +1308,7 @@ function mapslices(f, A::AbstractArray, dims::AbstractVector) if eltype(Rsize) == Int Rsize[dims] = [size(r1)..., ntuple(d->1, nextra)...] else - Rsize[dims] = [indices(r1)..., ntuple(d->1:1, nextra)...] + Rsize[dims] = [indices(r1)..., ntuple(d->OneTo(1), nextra)...] end R = similar(r1, tuple(Rsize...,)) diff --git a/base/arraymath.jl b/base/arraymath.jl index cb3cd4625e26e..c89ac3c2d6a30 100644 --- a/base/arraymath.jl +++ b/base/arraymath.jl @@ -211,8 +211,8 @@ function flipdim{T}(A::Array{T}, d::Integer) end function rotl90(A::AbstractMatrix) - B = similar_transpose(A) - ind2 = indices(A,2) + ind1, ind2 = indices(A) + B = similar(A, (ind2,ind1)) n = first(ind2)+last(ind2) for i=indices(A,1), j=ind2 B[n-j,i] = A[i,j] @@ -220,8 +220,8 @@ function rotl90(A::AbstractMatrix) return B end function rotr90(A::AbstractMatrix) - B = similar_transpose(A) - ind1 = indices(A,1) + ind1, ind2 = indices(A) + B = similar(A, (ind2,ind1)) m = first(ind1)+last(ind1) for i=ind1, j=indices(A,2) B[j,m-i] = A[i,j] @@ -246,10 +246,6 @@ end rotr90(A::AbstractMatrix, k::Integer) = rotl90(A,-k) rot180(A::AbstractMatrix, k::Integer) = mod(k, 2) == 1 ? rot180(A) : copy(A) -similar_transpose(A::AbstractMatrix) = similar_transpose(indicesbehavior(A), A) -similar_transpose(::IndicesStartAt1, A::AbstractMatrix) = similar(A, (size(A,2), size(A,1))) -similar_transpose(::IndicesBehavior, A::AbstractMatrix) = similar(A, (indices(A,2), indices(A,1))) - ## Transpose ## transpose!(B::AbstractMatrix, A::AbstractMatrix) = transpose_f!(transpose, B, A) ctranspose!(B::AbstractMatrix, A::AbstractMatrix) = transpose_f!(ctranspose, B, A) @@ -315,11 +311,13 @@ function ccopy!(B, A) end function transpose(A::AbstractMatrix) - B = similar_transpose(A) + ind1, ind2 = indices(A) + B = similar(A, (ind2, ind1)) transpose!(B, A) end function ctranspose(A::AbstractMatrix) - B = similar_transpose(A) + ind1, ind2 = indices(A) + B = similar(A, (ind2, ind1)) ctranspose!(B, A) end ctranspose{T<:Real}(A::AbstractVecOrMat{T}) = transpose(A) diff --git a/base/bitarray.jl b/base/bitarray.jl index 946deb1b4fa3c..e0754de709666 100644 --- a/base/bitarray.jl +++ b/base/bitarray.jl @@ -47,6 +47,8 @@ end isassigned{N}(B::BitArray{N}, i::Int) = 1 <= i <= length(B) +linearindexing{A<:BitArray}(::Type{A}) = LinearFast() + ## aux functions ## const _msk64 = ~UInt64(0) diff --git a/base/broadcast.jl b/base/broadcast.jl index ab8fee3c97753..e671e414c326f 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -3,7 +3,7 @@ module Broadcast using Base.Cartesian -using Base: promote_op, promote_eltype, promote_eltype_op, @get!, _msk_end, unsafe_bitgetindex, shape, linearindices, allocate_for, tail, dimlength +using Base: promote_op, promote_eltype, promote_eltype_op, @get!, _msk_end, unsafe_bitgetindex, linearindices, to_shape, tail, dimlength, OneTo import Base: .+, .-, .*, ./, .\, .//, .==, .<, .!=, .<=, .รท, .%, .<<, .>>, .^ export broadcast, broadcast!, bitbroadcast export broadcast_getindex, broadcast_setindex! @@ -13,8 +13,8 @@ export broadcast_getindex, broadcast_setindex! ## Calculate the broadcast shape of the arguments, or error if incompatible # array inputs broadcast_shape() = () -broadcast_shape(A) = shape(A) -@inline broadcast_shape(A, B...) = broadcast_shape((), shape(A), map(shape, B)...) +broadcast_shape(A) = indices(A) +@inline broadcast_shape(A, B...) = broadcast_shape((), indices(A), map(indices, B)...) # shape inputs broadcast_shape(shape::Tuple) = shape @inline broadcast_shape(shape::Tuple, shape1::Tuple, shapes::Tuple...) = broadcast_shape(_bcs((), shape, shape1), shapes...) @@ -39,8 +39,10 @@ _bcsm(a::Number, b::Number) = a == b || b == 1 ## Check that all arguments are broadcast compatible with shape ## Check that all arguments are broadcast compatible with shape # comparing one input against a shape +check_broadcast_shape(::Tuple{}) = nothing +check_broadcast_shape(::Tuple{}, A::Union{AbstractArray,Number}) = check_broadcast_shape((), indices(A)) check_broadcast_shape(shp) = nothing -check_broadcast_shape(shp, A) = check_broadcast_shape(shp, shape(A)) +check_broadcast_shape(shp, A) = check_broadcast_shape(shp, indices(A)) check_broadcast_shape(::Tuple{}, ::Tuple{}) = nothing check_broadcast_shape(shp, ::Tuple{}) = nothing check_broadcast_shape(::Tuple{}, Ashp::Tuple) = throw(DimensionMismatch("cannot broadcast array to have fewer dimensions")) @@ -133,7 +135,7 @@ end end @inline function broadcast!{nargs}(f, B::AbstractArray, As::Vararg{Any,nargs}) - check_broadcast_shape(shape(B), As...) + check_broadcast_shape(indices(B), As...) sz = size(B) mapindex = map(x->newindexer(sz, x), As) _broadcast!(f, B, mapindex, As, Val{nargs}) @@ -179,7 +181,7 @@ function broadcast_t(f, ::Type{Any}, As...) shp = broadcast_shape(As...) iter = CartesianRange(shp) if isempty(iter) - return allocate_for(Array{Union{}}, As, shp) + return similar(Array{Union{}}, shp) end nargs = length(As) sz = size(iter) @@ -187,12 +189,12 @@ function broadcast_t(f, ::Type{Any}, As...) st = start(iter) I, st = next(iter, st) val = f([ As[i][newindex(I, indexmaps[i])] for i=1:nargs ]...) - B = allocate_for(Array{typeof(val)}, As, shp) + B = similar(Array{typeof(val)}, shp) B[I] = val return _broadcast!(f, B, indexmaps, As, Val{nargs}, iter, st, 1) end -@inline broadcast_t(f, T, As...) = broadcast!(f, allocate_for(Array{T}, As, broadcast_shape(As...)), As...) +@inline broadcast_t(f, T, As...) = broadcast!(f, similar(Array{T}, broadcast_shape(As...)), As...) @inline broadcast(f, As...) = broadcast_t(f, promote_eltype_op(f, As...), As...) @@ -215,15 +217,15 @@ function broadcast(f, As...) end =# -@inline bitbroadcast(f, As...) = broadcast!(f, allocate_for(BitArray, As, broadcast_shape(As...)), As...) +@inline bitbroadcast(f, As...) = broadcast!(f, similar(BitArray, broadcast_shape(As...)), As...) -broadcast_getindex(src::AbstractArray, I::AbstractArray...) = broadcast_getindex!(Array{eltype(src)}(broadcast_shape(I...)), src, I...) +broadcast_getindex(src::AbstractArray, I::AbstractArray...) = broadcast_getindex!(Array{eltype(src)}(to_shape(broadcast_shape(I...))), src, I...) @generated function broadcast_getindex!(dest::AbstractArray, src::AbstractArray, I::AbstractArray...) N = length(I) Isplat = Expr[:(I[$d]) for d = 1:N] quote @nexprs $N d->(I_d = I[d]) - check_broadcast_shape(size(dest), $(Isplat...)) # unnecessary if this function is never called directly + check_broadcast_shape(indices(dest), $(Isplat...)) # unnecessary if this function is never called directly checkbounds(src, $(Isplat...)) @nloops $N i dest d->(@nexprs $N k->(j_d_k = size(I_k, d) == 1 ? 1 : i_d)) begin @nexprs $N k->(@inbounds J_k = @nref $N I_k d->j_d_k) @@ -240,22 +242,22 @@ end @nexprs $N d->(I_d = I[d]) checkbounds(A, $(Isplat...)) shape = broadcast_shape($(Isplat...)) - @nextract $N shape d->(length(shape) < d ? 1 : shape[d]) + @nextract $N shape d->(length(shape) < d ? OneTo(1) : shape[d]) if !isa(x, AbstractArray) - @nloops $N i d->(1:shape_d) d->(@nexprs $N k->(j_d_k = size(I_k, d) == 1 ? 1 : i_d)) begin + xA = convert(eltype(A), x) + @nloops $N i d->shape_d d->(@nexprs $N k->(j_d_k = size(I_k, d) == 1 ? 1 : i_d)) begin @nexprs $N k->(@inbounds J_k = @nref $N I_k d->j_d_k) - @inbounds (@nref $N A J) = x + @inbounds (@nref $N A J) = xA end else X = x - # To call setindex_shape_check, we need to create fake 1-d indexes of the proper size - @nexprs $N d->(fakeI_d = 1:shape_d) - @ncall $N Base.setindex_shape_check X shape - k = 1 - @nloops $N i d->(1:shape_d) d->(@nexprs $N k->(j_d_k = size(I_k, d) == 1 ? 1 : i_d)) begin - @nexprs $N k->(@inbounds J_k = @nref $N I_k d->j_d_k) - @inbounds (@nref $N A J) = X[k] - k += 1 + @nexprs $N d->(shapelen_d = dimlength(shape_d)) + @ncall $N Base.setindex_shape_check X shapelen + Xstate = start(X) + @inbounds @nloops $N i d->shape_d d->(@nexprs $N k->(j_d_k = size(I_k, d) == 1 ? 1 : i_d)) begin + @nexprs $N k->(J_k = @nref $N I_k d->j_d_k) + x_el, Xstate = next(X, Xstate) + (@nref $N A J) = x_el end end A @@ -271,22 +273,22 @@ end eltype_plus(As::AbstractArray...) = promote_eltype_op(+, As...) -.+(As::AbstractArray...) = broadcast!(+, Array{eltype_plus(As...)}(broadcast_shape(As...)), As...) +.+(As::AbstractArray...) = broadcast!(+, Array{eltype_plus(As...)}(to_shape(broadcast_shape(As...))), As...) function .-(A::AbstractArray, B::AbstractArray) - broadcast!(-, Array{promote_op(-, eltype(A), eltype(B))}(broadcast_shape(A,B)), A, B) + broadcast!(-, Array{promote_op(-, eltype(A), eltype(B))}(to_shape(broadcast_shape(A,B))), A, B) end eltype_mul(As::AbstractArray...) = promote_eltype_op(*, As...) -.*(As::AbstractArray...) = broadcast!(*, Array{eltype_mul(As...)}(broadcast_shape(As...)), As...) +.*(As::AbstractArray...) = broadcast!(*, Array{eltype_mul(As...)}(to_shape(broadcast_shape(As...))), As...) function ./(A::AbstractArray, B::AbstractArray) - broadcast!(/, Array{promote_op(/, eltype(A), eltype(B))}(broadcast_shape(A, B)), A, B) + broadcast!(/, Array{promote_op(/, eltype(A), eltype(B))}(to_shape(broadcast_shape(A, B))), A, B) end function .\(A::AbstractArray, B::AbstractArray) - broadcast!(\, Array{promote_op(\, eltype(A), eltype(B))}(broadcast_shape(A, B)), A, B) + broadcast!(\, Array{promote_op(\, eltype(A), eltype(B))}(to_shape(broadcast_shape(A, B))), A, B) end typealias RatIntT{T<:Integer} Union{Type{Rational{T}},Type{T}} @@ -296,11 +298,11 @@ type_rdiv{T<:Integer,S<:Integer}(::RatIntT{T}, ::RatIntT{S}) = type_rdiv{T<:Integer,S<:Integer}(::CRatIntT{T}, ::CRatIntT{S}) = Complex{Rational{promote_type(T,S)}} function .//(A::AbstractArray, B::AbstractArray) - broadcast!(//, Array{type_rdiv(eltype(A), eltype(B))}(broadcast_shape(A, B)), A, B) + broadcast!(//, Array{type_rdiv(eltype(A), eltype(B))}(to_shape(broadcast_shape(A, B))), A, B) end function .^(A::AbstractArray, B::AbstractArray) - broadcast!(^, Array{promote_op(^, eltype(A), eltype(B))}(broadcast_shape(A, B)), A, B) + broadcast!(^, Array{promote_op(^, eltype(A), eltype(B))}(to_shape(broadcast_shape(A, B))), A, B) end # ## element-wise comparison operators returning BitArray ## @@ -363,10 +365,10 @@ for (f, scalarf) in ((:.==, :(==)), :((A,ind)->A), :((B,ind)->B[ind])), (:AbstractArray, :Any, :A, :((A,ind)->A[ind]), :((B,ind)->B))) - shape = :(shape($active)) + shape = :(indices($active)) @eval begin function ($f)(A::$sigA, B::$sigB) - P = allocate_for(BitArray, $active, $shape) + P = similar(BitArray, $shape) F = parent(P) l = length(F) l == 0 && return F diff --git a/base/deprecated.jl b/base/deprecated.jl index b22c1a3cc6523..f1051985b1f30 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -766,6 +766,12 @@ function first(::Colon) 1 end +# Not exported, but may be useful just in case +function Broadcast.check_broadcast_shape(sz::Dims, As::Union{AbstractArray,Number}...) + depwarn("check_broadcast_shape(size(A), B...) should be replaced with check_broadcast_shape(indices(A), B...)", :check_broadcast_shape) + Broadcast.check_broadcast_shape(map(OneTo, sz), As...) +end + @deprecate slice view @deprecate sub view diff --git a/base/essentials.jl b/base/essentials.jl index bdc95b133b7f4..2f7ecbeff7645 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -171,7 +171,7 @@ start(v::SimpleVector) = 1 next(v::SimpleVector,i) = (v[i],i+1) done(v::SimpleVector,i) = (i > v.length) isempty(v::SimpleVector) = (v.length == 0) -indices(v::SimpleVector, d) = d == 1 ? (1:length(v)) : (1:1) +indices(v::SimpleVector) = (OneTo(length(v)),) linearindices(v::SimpleVector) = indices(v, 1) function ==(v1::SimpleVector, v2::SimpleVector) diff --git a/base/exports.jl b/base/exports.jl index fc9db004b7b4d..c2fa3db6c6a64 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -28,6 +28,7 @@ export # Types AbstractChannel, AbstractMatrix, + AbstractUnitRange, AbstractVector, AbstractVecOrMat, Array, @@ -485,7 +486,6 @@ export zeta, # arrays - allocate_for, bitbroadcast, broadcast!, broadcast, @@ -585,7 +585,6 @@ export searchsortedlast, select!, select, - shape, shuffle, shuffle!, size, diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 838101efd8328..3c4e3b3bef930 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -10,9 +10,6 @@ using Base: LinearFast, LinearSlow, AbstractCartesianIndex, fill_to_length, tail export CartesianIndex, CartesianRange -# Traits for linear indexing -linearindexing{A<:BitArray}(::Type{A}) = LinearFast() - # CartesianIndex immutable CartesianIndex{N} <: AbstractCartesianIndex{N} I::NTuple{N,Int} @@ -78,12 +75,12 @@ end CartesianRange{N}(index::CartesianIndex{N}) = CartesianRange(one(index), index) CartesianRange(::Tuple{}) = CartesianRange{CartesianIndex{0}}(CartesianIndex{0}(()),CartesianIndex{0}(())) CartesianRange{N}(sz::NTuple{N,Int}) = CartesianRange(CartesianIndex(sz)) -CartesianRange{N}(rngs::NTuple{N,Union{Int,UnitRange{Int}}}) = CartesianRange(CartesianIndex(map(r->first(r), rngs)), CartesianIndex(map(r->last(r), rngs))) +CartesianRange{N}(rngs::NTuple{N,Union{Integer,AbstractUnitRange}}) = CartesianRange(CartesianIndex(map(r->first(r), rngs)), CartesianIndex(map(r->last(r), rngs))) ndims(R::CartesianRange) = length(R.start) ndims{I<:CartesianIndex}(::Type{CartesianRange{I}}) = length(I) -eachindex(::LinearSlow, A::AbstractArray) = CartesianRange(size(A)) +eachindex(::LinearSlow, A::AbstractArray) = CartesianRange(indices(A)) @inline eachindex(::LinearSlow, A::AbstractArray, B::AbstractArray...) = CartesianRange(maxsize((), A, B...)) maxsize(sz) = sz @@ -178,18 +175,18 @@ index_lengths_dim(A, dim, ::Colon) = (trailingsize(A, dim),) # whose length is equal to the dimension we're to process next. This # allows us to dispatch, which is important for the type-stability of # the lines involving Colon as the final index. -index_shape(A::AbstractVector, I::Colon) = shape(A) +index_shape(A::AbstractVector, I::Colon) = indices(A) index_shape(A::AbstractArray, I::Colon) = (length(A),) @inline index_shape(A::AbstractArray, I...) = index_shape_dim(A, (true,), I...) @inline index_shape_dim(A, dim, ::Colon) = (trailingsize(A, length(dim)),) -@inline index_shape_dim{T,N}(A::AbstractArray{T,N}, dim::NTuple{N}, ::Colon) = (shape(A, N),) +@inline index_shape_dim{T,N}(A::AbstractArray{T,N}, dim::NTuple{N}, ::Colon) = (indices(A, N),) @inline index_shape_dim(A, dim, I::Real...) = () -@inline index_shape_dim(A, dim, ::Colon, i, I...) = (shape(A, length(dim)), index_shape_dim(A, (dim...,true), i, I...)...) +@inline index_shape_dim(A, dim, ::Colon, i, I...) = (indices(A, length(dim)), index_shape_dim(A, (dim...,true), i, I...)...) @inline index_shape_dim(A, dim, ::Real, I...) = (index_shape_dim(A, (dim...,true), I...)...) @inline index_shape_dim{N}(A, dim, ::CartesianIndex{N}, I...) = (index_shape_dim(A, (dim...,ntuple(d->true,Val{N})...), I...)...) -@inline index_shape_dim(A, dim, i::AbstractArray, I...) = (shape(i)..., index_shape_dim(A, (dim...,true), I...)...) +@inline index_shape_dim(A, dim, i::AbstractArray, I...) = (indices(i)..., index_shape_dim(A, (dim...,true), I...)...) @inline index_shape_dim(A, dim, i::AbstractArray{Bool}, I...) = (sum(i), index_shape_dim(A, (dim...,true), I...)...) -@inline index_shape_dim{N}(A, dim, i::AbstractArray{CartesianIndex{N}}, I...) = (shape(i)..., index_shape_dim(A, (dim...,ntuple(d->true,Val{N})...), I...)...) +@inline index_shape_dim{N}(A, dim, i::AbstractArray{CartesianIndex{N}}, I...) = (indices(i)..., index_shape_dim(A, (dim...,ntuple(d->true,Val{N})...), I...)...) @inline decolon(A::AbstractVector, ::Colon) = (indices(A,1),) @inline decolon(A::AbstractArray, ::Colon) = (1:length(A),) @@ -291,8 +288,6 @@ end end end -dimlength(r::Range) = length(r) -dimlength(i::Integer) = i @noinline throw_checksize_error(A, sz) = throw(DimensionMismatch("output array is the wrong size; expected $sz, got $(size(A))")) ## setindex! ## @@ -787,7 +782,7 @@ If `dim` is specified, returns unique regions of the array `itr` along `dim`. @generated function unique{T,N}(A::AbstractArray{T,N}, dim::Int) quote 1 <= dim <= $N || return copy(A) - hashes = allocate_for(inds->zeros(UInt, inds), A, shape(A, dim)) + hashes = similar(inds->zeros(UInt, inds), indices(A, dim)) # Compute hash for each row k = 0 @@ -796,7 +791,7 @@ If `dim` is specified, returns unique regions of the array `itr` along `dim`. end # Collect index of first row for each hash - uniquerow = allocate_for(Array{Int}, A, shape(A, dim)) + uniquerow = similar(Array{Int}, indices(A, dim)) firstrow = Dict{Prehashed,Int}() for k = indices(A, dim) uniquerow[k] = get!(firstrow, Prehashed(hashes[k]), k) @@ -804,7 +799,7 @@ If `dim` is specified, returns unique regions of the array `itr` along `dim`. uniquerows = collect(values(firstrow)) # Check for collisions - collided = allocate_for(falses, A, shape(A, dim)) + collided = similar(falses, indices(A, dim)) @inbounds begin @nloops $N i A d->(if d == dim k = i_d @@ -819,7 +814,7 @@ If `dim` is specified, returns unique regions of the array `itr` along `dim`. end if any(collided) - nowcollided = allocate_for(BitArray, A, shape(A, dim)) + nowcollided = similar(BitArray, indices(A, dim)) while any(collided) # Collect index of first row for each collided hash empty!(firstrow) diff --git a/base/number.jl b/base/number.jl index 3f8cbfc37c610..d2d3270f1cfc4 100644 --- a/base/number.jl +++ b/base/number.jl @@ -7,7 +7,7 @@ isinteger(x::Integer) = true size(x::Number) = () size(x::Number,d) = convert(Int,d)<1 ? throw(BoundsError()) : 1 indices(x::Number) = () -indices(x::Number,d) = convert(Int,d)<1 ? throw(BoundsError()) : (1:1) +indices(x::Number,d) = convert(Int,d)<1 ? throw(BoundsError()) : OneTo(1) eltype{T<:Number}(::Type{T}) = T ndims(x::Number) = 0 ndims{T<:Number}(::Type{T}) = 0 diff --git a/base/operators.jl b/base/operators.jl index 6570a14e3e612..ae9ccd639d525 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -387,7 +387,7 @@ function promote_shape(a::AbstractArray, b::AbstractArray) throw(DimensionMismatch("dimensions must match")) end end - return shape(a) + return indices(a) end function throw_setindex_mismatch(X, I) @@ -403,7 +403,7 @@ end # for permutations that leave array elements in the same linear order. # those are the permutations that preserve the order of the non-singleton # dimensions. -function setindex_shape_check(X::AbstractArray, I...) +function setindex_shape_check(X::AbstractArray, I::Integer...) li = ndims(X) lj = length(I) i = j = 1 @@ -440,16 +440,16 @@ end setindex_shape_check(X::AbstractArray) = (length(X)==1 || throw_setindex_mismatch(X,())) -setindex_shape_check(X::AbstractArray, i) = +setindex_shape_check(X::AbstractArray, i::Integer) = (length(X)==i || throw_setindex_mismatch(X, (i,))) -setindex_shape_check{T}(X::AbstractArray{T,1}, i) = +setindex_shape_check{T}(X::AbstractArray{T,1}, i::Integer) = (length(X)==i || throw_setindex_mismatch(X, (i,))) -setindex_shape_check{T}(X::AbstractArray{T,1}, i, j) = +setindex_shape_check{T}(X::AbstractArray{T,1}, i::Integer, j::Integer) = (length(X)==i*j || throw_setindex_mismatch(X, (i,j))) -function setindex_shape_check{T}(X::AbstractArray{T,2}, i, j) +function setindex_shape_check{T}(X::AbstractArray{T,2}, i::Integer, j::Integer) if length(X) != i*j throw_setindex_mismatch(X, (i,j)) end diff --git a/base/permuteddimsarray.jl b/base/permuteddimsarray.jl index c5a461509d494..8ab7d60f26301 100644 --- a/base/permuteddimsarray.jl +++ b/base/permuteddimsarray.jl @@ -2,8 +2,6 @@ module PermutedDimsArrays -using Base: IndicesStartAt1, IndicesBehavior, indicesbehavior - export permutedims # Some day we will want storage-order-aware iteration, so put perm in the parameters @@ -27,7 +25,7 @@ end Base.parent(A::PermutedDimsArray) = A.parent Base.size(A::PermutedDimsArray) = A.dims -Base.indices{T,N,perm}(A::PermutedDimsArray{T,N,perm}, d) = indices(parent(A), perm[d]) +Base.indices{T,N,perm}(A::PermutedDimsArray{T,N,perm}) = genperm(indices(parent(A)), perm) @inline function Base.getindex{T,N,perm,iperm}(A::PermutedDimsArray{T,N,perm,iperm}, I::Vararg{Int,N}) @boundscheck checkbounds(A, I...) @@ -47,14 +45,10 @@ _genperm(out, I) = out @inline genperm(I, perm::AbstractVector{Int}) = genperm(I, (perm...,)) function Base.permutedims{T,N}(A::AbstractArray{T,N}, perm) - dest = similar_permute(A, perm) + dest = similar(A, genperm(indices(A), perm)) permutedims!(dest, A, perm) end -similar_permute(A::AbstractArray, perm) = similar_permute(indicesbehavior(A), A, perm) -similar_permute{T,N}(::IndicesStartAt1, A::AbstractArray{T,N}, perm) = similar(A, genperm(size(A), perm)) -similar_permute{T,N}(::IndicesBehavior, A::AbstractArray{T,N}, perm) = similar(A, genperm(indices(A), perm)) - function Base.permutedims!(dest, src::AbstractArray, perm) Base.checkdims_perm(dest, src, perm) P = PermutedDimsArray(dest, invperm(perm)) diff --git a/base/range.jl b/base/range.jl index 58ec756d1b078..a3badcf109535 100644 --- a/base/range.jl +++ b/base/range.jl @@ -10,6 +10,7 @@ abstract Range{T} <: AbstractArray{T,1} ## ordinal ranges abstract OrdinalRange{T,S} <: Range{T} +abstract AbstractUnitRange{T} <: OrdinalRange{T,Int} immutable StepRange{T,S} <: OrdinalRange{T,S} start::T @@ -64,7 +65,7 @@ steprem(start,stop,step) = (stop-start) % step StepRange{T,S}(start::T, step::S, stop::T) = StepRange{T,S}(start, step, stop) -immutable UnitRange{T<:Real} <: OrdinalRange{T,Int} +immutable UnitRange{T<:Real} <: AbstractUnitRange{T} start::T stop::T UnitRange(start, stop) = new(start, unitrange_last(start,stop)) @@ -78,6 +79,12 @@ unitrange_last{T}(start::T, stop::T) = ifelse(stop >= start, convert(T,start+floor(stop-start)), convert(T,start-one(stop-start))) +immutable OneTo{T<:Integer} <: AbstractUnitRange{T} + stop::T + OneTo(stop) = new(max(zero(T), stop)) +end +OneTo{T<:Integer}(stop::T) = OneTo{T}(stop) + colon(a::Real, b::Real) = colon(promote(a,b)...) colon{T<:Real}(start::T, stop::T) = UnitRange{T}(start, stop) @@ -310,12 +317,12 @@ size(r::Range) = (length(r),) isempty(r::StepRange) = (r.start != r.stop) & ((r.step > zero(r.step)) != (r.stop > r.start)) -isempty(r::UnitRange) = r.start > r.stop +isempty(r::AbstractUnitRange) = first(r) > last(r) isempty(r::FloatRange) = length(r) == 0 isempty(r::LinSpace) = length(r) == 0 step(r::StepRange) = r.step -step(r::UnitRange) = 1 +step(r::AbstractUnitRange) = 1 step(r::FloatRange) = r.step/r.divisor step{T}(r::LinSpace{T}) = ifelse(r.len <= 0, convert(T,NaN), (r.stop-r.start)/r.divisor) @@ -323,7 +330,8 @@ function length(r::StepRange) n = Integer(div(r.stop+r.step - r.start, r.step)) isempty(r) ? zero(n) : n end -length(r::UnitRange) = Integer(r.stop - r.start + 1) +length(r::AbstractUnitRange) = Integer(last(r) - first(r) + 1) +length(r::OneTo) = r.stop length(r::FloatRange) = Integer(r.len) length(r::LinSpace) = Integer(r.len + signbit(r.len - 1)) @@ -338,11 +346,12 @@ function length{T<:Union{Int,UInt,Int64,UInt64}}(r::StepRange{T}) end end -length{T<:Union{Int,Int64}}(r::UnitRange{T}) = - checked_add(checked_sub(r.stop, r.start), one(T)) +length{T<:Union{Int,Int64}}(r::AbstractUnitRange{T}) = + checked_add(checked_sub(last(r), first(r)), one(T)) +length{T<:Union{Int,Int64}}(r::OneTo{T}) = T(r.stop) -length{T<:Union{UInt,UInt64}}(r::UnitRange{T}) = - r.stop < r.start ? zero(T) : checked_add(r.stop - r.start, one(T)) +length{T<:Union{UInt,UInt64}}(r::AbstractUnitRange{T}) = + r.stop < r.start ? zero(T) : checked_add(last(r) - first(r), one(T)) # some special cases to favor default Int type let smallint = (Int === Int64 ? @@ -355,20 +364,21 @@ let smallint = (Int === Int64 ? div(Int(r.stop)+Int(r.step) - Int(r.start), Int(r.step)) end - length{T <: smallint}(r::UnitRange{T}) = Int(r.stop) - Int(r.start) + 1 + length{T <: smallint}(r::AbstractUnitRange{T}) = Int(last(r)) - Int(first(r)) + 1 + length{T <: smallint}(r::OneTo{T}) = Int(r.stop) end first{T}(r::OrdinalRange{T}) = convert(T, r.start) +first{T}(r::OneTo{T}) = one(T) first{T}(r::FloatRange{T}) = convert(T, r.start/r.divisor) first{T}(r::LinSpace{T}) = convert(T, (r.len-1)*r.start/r.divisor) -last{T}(r::StepRange{T}) = r.stop -last(r::UnitRange) = r.stop +last{T}(r::OrdinalRange{T}) = convert(T, r.stop) last{T}(r::FloatRange{T}) = convert(T, (r.start + (r.len-1)*r.step)/r.divisor) last{T}(r::LinSpace{T}) = convert(T, (r.len-1)*r.stop/r.divisor) -minimum(r::UnitRange) = isempty(r) ? throw(ArgumentError("range must be non-empty")) : first(r) -maximum(r::UnitRange) = isempty(r) ? throw(ArgumentError("range must be non-empty")) : last(r) +minimum(r::AbstractUnitRange) = isempty(r) ? throw(ArgumentError("range must be non-empty")) : first(r) +maximum(r::AbstractUnitRange) = isempty(r) ? throw(ArgumentError("range must be non-empty")) : last(r) minimum(r::Range) = isempty(r) ? throw(ArgumentError("range must be non-empty")) : min(first(r), last(r)) maximum(r::Range) = isempty(r) ? throw(ArgumentError("range must be non-empty")) : max(first(r), last(r)) @@ -398,9 +408,10 @@ done{T,S}(r::StepRange{T,S}, i::Integer) = isempty(r) | (i == oftype(i, r.stop) + r.step) start{T}(r::UnitRange{T}) = oftype(r.start + one(T), r.start) -next{T}(r::UnitRange{T}, i) = (convert(T, i), i + one(T)) -done{T}(r::UnitRange{T}, i) = i == oftype(i, r.stop) + one(T) +next{T}(r::AbstractUnitRange{T}, i) = (convert(T, i), i + one(T)) +done{T}(r::AbstractUnitRange{T}, i) = i == oftype(i, r.stop) + one(T) +start{T}(r::OneTo{T}) = one(T) # some special cases to favor default Int type to avoid overflow let smallint = (Int === Int64 ? @@ -411,7 +422,8 @@ let smallint = (Int === Int64 ? start{T<:smallint}(r::StepRange{T}) = convert(Int, r.start) next{T<:smallint}(r::StepRange{T}, i) = (i % T, i + r.step) start{T<:smallint}(r::UnitRange{T}) = convert(Int, r.start) - next{T<:smallint}(r::UnitRange{T}, i) = (i % T, i + 1) + next{T<:smallint}(r::AbstractUnitRange{T}, i) = (i % T, i + 1) + start{T<:smallint}(r::OneTo{T}) = 1 end ## indexing @@ -423,6 +435,12 @@ function getindex{T}(v::UnitRange{T}, i::Integer) ret end +function getindex{T}(v::OneTo{T}, i::Integer) + @_inline_meta + @boundscheck ((i > 0) & (i <= v.stop)) || throw_boundserror(v, i) + convert(T, i) +end + function getindex{T}(v::Range{T}, i::Integer) @_inline_meta ret = convert(T, first(v) + (i - 1)*step(v)) @@ -447,17 +465,23 @@ end getindex(r::Range, ::Colon) = copy(r) -function getindex{T<:Integer}(r::UnitRange, s::UnitRange{T}) +function getindex{T<:Integer}(r::UnitRange, s::AbstractUnitRange{T}) @_inline_meta @boundscheck checkbounds(r, s) - st = oftype(r.start, r.start + s.start-1) + st = oftype(r.start, r.start + first(s)-1) range(st, length(s)) end -function getindex{T<:Integer}(r::UnitRange, s::StepRange{T}) +function getindex{T}(r::OneTo{T}, s::OneTo) @_inline_meta @boundscheck checkbounds(r, s) - st = oftype(r.start, r.start + s.start-1) + OneTo(T(s.stop)) +end + +function getindex{T<:Integer}(r::AbstractUnitRange, s::StepRange{T}) + @_inline_meta + @boundscheck checkbounds(r, s) + st = oftype(first(r), first(r) + s.start-1) range(st, step(s), length(s)) end @@ -487,6 +511,7 @@ end show(io::IO, r::Range) = print(io, repr(first(r)), ':', repr(step(r)), ':', repr(last(r))) show(io::IO, r::UnitRange) = print(io, repr(first(r)), ':', repr(last(r))) +show(io::IO, r::OneTo) = print(io, "Base.OneTo(", r.stop, ")") =={T<:Range}(r::T, s::T) = (first(r) == first(s)) & (step(r) == step(s)) & (last(r) == last(s)) ==(r::OrdinalRange, s::OrdinalRange) = (first(r) == first(s)) & (step(r) == step(s)) & (last(r) == last(s)) @@ -508,15 +533,17 @@ function ==(r::Range, s::Range) return true end -intersect{T1<:Integer, T2<:Integer}(r::UnitRange{T1}, s::UnitRange{T2}) = max(r.start,s.start):min(last(r),last(s)) +intersect(r::OneTo, s::OneTo) = OneTo(min(r.stop,s.stop)) -intersect{T<:Integer}(i::Integer, r::UnitRange{T}) = +intersect{T1<:Integer, T2<:Integer}(r::AbstractUnitRange{T1}, s::AbstractUnitRange{T2}) = max(first(r),first(s)):min(last(r),last(s)) + +intersect{T<:Integer}(i::Integer, r::AbstractUnitRange{T}) = i < first(r) ? (first(r):i) : i > last(r) ? (i:last(r)) : (i:i) intersect{T<:Integer}(r::UnitRange{T}, i::Integer) = intersect(i, r) -function intersect{T1<:Integer, T2<:Integer}(r::UnitRange{T1}, s::StepRange{T2}) +function intersect{T1<:Integer, T2<:Integer}(r::AbstractUnitRange{T1}, s::StepRange{T2}) if isempty(s) range(first(r), 0) elseif step(s) == 0 @@ -535,7 +562,7 @@ function intersect{T1<:Integer, T2<:Integer}(r::UnitRange{T1}, s::StepRange{T2}) end end -function intersect{T1<:Integer, T2<:Integer}(r::StepRange{T1}, s::UnitRange{T2}) +function intersect{T1<:Integer, T2<:Integer}(r::StepRange{T1}, s::AbstractUnitRange{T2}) if step(r) < 0 reverse(intersect(s, reverse(r))) else @@ -597,7 +624,7 @@ function intersect(r1::Range, r2::Range, r3::Range, r::Range...) end # findin (the index of intersection) -function _findin{T1<:Integer, T2<:Integer}(r::Range{T1}, span::UnitRange{T2}) +function _findin{T1<:Integer, T2<:Integer}(r::Range{T1}, span::AbstractUnitRange{T2}) local ifirst local ilast fspan = first(span) @@ -630,11 +657,11 @@ end ## linear operations on ranges ## --(r::OrdinalRange) = range(-r.start, -step(r), length(r)) +-(r::OrdinalRange) = range(-first(r), -step(r), length(r)) -(r::FloatRange) = FloatRange(-r.start, -r.step, r.len, r.divisor) -(r::LinSpace) = LinSpace(-r.start, -r.stop, r.len, r.divisor) -.+(x::Real, r::UnitRange) = range(x + r.start, length(r)) +.+(x::Real, r::AbstractUnitRange) = range(x + first(r), length(r)) .+(x::Real, r::Range) = (x+first(r)):step(r):(x+last(r)) #.+(x::Real, r::StepRange) = range(x + r.start, r.step, length(r)) .+(x::Real, r::FloatRange) = FloatRange(r.divisor*x + r.start, r.step, r.len, r.divisor) @@ -651,7 +678,7 @@ function .-(x::Real, r::LinSpace) x2 = x * r.divisor / (r.len - 1) LinSpace(x2 - r.start, x2 - r.stop, r.len, r.divisor) end -.-(r::UnitRange, x::Real) = range(r.start-x, length(r)) +.-(r::AbstractUnitRange, x::Real) = range(first(r)-x, length(r)) .-(r::StepRange , x::Real) = range(r.start-x, r.step, length(r)) .-(r::FloatRange, x::Real) = FloatRange(r.start - r.divisor*x, r.step, r.len, r.divisor) function .-(r::LinSpace, x::Real) @@ -659,14 +686,14 @@ function .-(r::LinSpace, x::Real) LinSpace(r.start - x2, r.stop - x2, r.len, r.divisor) end -.*(x::Real, r::OrdinalRange) = range(x*r.start, x*step(r), length(r)) +.*(x::Real, r::OrdinalRange) = range(x*first(r), x*step(r), length(r)) .*(x::Real, r::FloatRange) = FloatRange(x*r.start, x*r.step, r.len, r.divisor) .*(x::Real, r::LinSpace) = LinSpace(x * r.start, x * r.stop, r.len, r.divisor) .*(r::Range, x::Real) = x .* r .*(r::FloatRange, x::Real) = x .* r .*(r::LinSpace, x::Real) = x .* r -./(r::OrdinalRange, x::Real) = range(r.start/x, step(r)/x, length(r)) +./(r::OrdinalRange, x::Real) = range(first(r)/x, step(r)/x, length(r)) ./(r::FloatRange, x::Real) = FloatRange(r.start/x, r.step/x, r.len, r.divisor) ./(r::LinSpace, x::Real) = LinSpace(r.start / x, r.stop / x, r.len, r.divisor) @@ -675,15 +702,24 @@ promote_rule{T1,T2}(::Type{UnitRange{T1}},::Type{UnitRange{T2}}) = convert{T<:Real}(::Type{UnitRange{T}}, r::UnitRange{T}) = r convert{T<:Real}(::Type{UnitRange{T}}, r::UnitRange) = UnitRange{T}(r.start, r.stop) +promote_rule{T1,T2}(::Type{OneTo{T1}},::Type{OneTo{T2}}) = + OneTo{promote_type(T1,T2)} +convert{T<:Real}(::Type{OneTo{T}}, r::OneTo{T}) = r +convert{T<:Real}(::Type{OneTo{T}}, r::OneTo) = OneTo{T}(r.stop) + +promote_rule{T1,UR<:AbstractUnitRange}(::Type{UnitRange{T1}}, ::Type{UR}) = + UnitRange{promote_type(T1,eltype(UR))} +convert{T<:Real}(::Type{UnitRange{T}}, r::AbstractUnitRange) = UnitRange{T}(first(r), last(r)) + promote_rule{T1a,T1b,T2a,T2b}(::Type{StepRange{T1a,T1b}},::Type{StepRange{T2a,T2b}}) = StepRange{promote_type(T1a,T2a),promote_type(T1b,T2b)} convert{T1,T2}(::Type{StepRange{T1,T2}}, r::StepRange{T1,T2}) = r -promote_rule{T1a,T1b,T2}(::Type{StepRange{T1a,T1b}},::Type{UnitRange{T2}}) = - StepRange{promote_type(T1a,T2),promote_type(T1b,T2)} +promote_rule{T1a,T1b,UR<:AbstractUnitRange}(::Type{StepRange{T1a,T1b}},::Type{UR}) = + StepRange{promote_type(T1a,eltype(UR)),promote_type(T1b,eltype(UR))} convert{T1,T2}(::Type{StepRange{T1,T2}}, r::Range) = StepRange{T1,T2}(convert(T1, first(r)), convert(T2, step(r)), convert(T1, last(r))) -convert{T}(::Type{StepRange}, r::UnitRange{T}) = +convert{T}(::Type{StepRange}, r::AbstractUnitRange{T}) = StepRange{T,T}(first(r), step(r), last(r)) promote_rule{T1,T2}(::Type{FloatRange{T1}},::Type{FloatRange{T2}}) = @@ -765,15 +801,15 @@ reverse(r::LinSpace) = LinSpace(r.stop, r.start, r.len, r.divisor) ## sorting ## -issorted(r::UnitRange) = true +issorted(r::AbstractUnitRange) = true issorted(r::Range) = step(r) >= zero(step(r)) -sort(r::UnitRange) = r -sort!(r::UnitRange) = r +sort(r::AbstractUnitRange) = r +sort!(r::AbstractUnitRange) = r sort(r::Range) = issorted(r) ? r : reverse(r) -sortperm(r::UnitRange) = 1:length(r) +sortperm(r::AbstractUnitRange) = 1:length(r) sortperm(r::Range) = issorted(r) ? (1:1:length(r)) : (length(r):-1:1) function sum{T<:Real}(r::Range{T}) @@ -806,6 +842,6 @@ function in(x, r::Range) n >= 1 && n <= length(r) && r[n] == x end -in{T<:Integer}(x::Integer, r::UnitRange{T}) = (first(r) <= x) & (x <= last(r)) +in{T<:Integer}(x::Integer, r::AbstractUnitRange{T}) = (first(r) <= x) & (x <= last(r)) in{T<:Integer}(x, r::Range{T}) = isinteger(x) && !isempty(r) && x>=minimum(r) && x<=maximum(r) && (mod(convert(T,x),step(r))-mod(first(r),step(r)) == 0) in(x::Char, r::Range{Char}) = !isempty(r) && x >= minimum(r) && x <= maximum(r) && (mod(Int(x) - Int(first(r)), step(r)) == 0) diff --git a/base/reshapedarray.jl b/base/reshapedarray.jl index a28a7fb2d4721..053a554eb821a 100644 --- a/base/reshapedarray.jl +++ b/base/reshapedarray.jl @@ -36,24 +36,20 @@ start(R::ReshapedArrayIterator) = start(R.iter) end length(R::ReshapedArrayIterator) = length(R.iter) -reshape(parent::AbstractArray, ref::AbstractArray) = reshape(indicesbehavior(ref), parent, ref) -reshape(::IndicesStartAt1, parent::AbstractArray, ref::AbstractArray) = reshape(parent, size(ref)) -reshape(::IndicesBehavior, parent::AbstractArray, ref::AbstractArray) = reshape(parent, indices(ref)) - -reshape(parent::AbstractArray, dims::Dims) = _reshape(parent, dims) -reshape(parent::AbstractArray, len::Integer) = reshape(parent, (Int(len),)) -reshape(parent::AbstractArray, dims::Int...) = reshape(parent, dims) +reshape(parent::AbstractArray, shp::Tuple) = _reshape(parent, to_shape(shp)) +reshape(parent::AbstractArray, dims::DimOrInd...) = reshape(parent, dims) reshape{T,N}(parent::AbstractArray{T,N}, ndims::Type{Val{N}}) = parent function reshape{T,AN,N}(parent::AbstractArray{T,AN}, ndims::Type{Val{N}}) - reshape(parent, rdims((), size(parent), Val{N})) + reshape(parent, rdims((), indices(parent), Val{N})) end -# Move elements from sz to out until out reaches the desired dimensionality N, -# either filling with 1 or collapsing the product of trailing dims into the last element -@pure rdims{N}(out::NTuple{N}, sz::Tuple{}, ::Type{Val{N}}) = out -@pure rdims{N}(out::NTuple{N}, sz::Tuple{Any, Vararg{Any}}, ::Type{Val{N}}) = (front(out)..., last(out) * prod(sz)) -@pure rdims{N}(out::Tuple, sz::Tuple{}, ::Type{Val{N}}) = rdims((out..., 1), (), Val{N}) -@pure rdims{N}(out::Tuple, sz::Tuple{Any, Vararg{Any}}, ::Type{Val{N}}) = rdims((out..., first(sz)), tail(sz), Val{N}) +# Move elements from inds to out until out reaches the desired +# dimensionality N, either filling with OneTo(1) or collapsing the +# product of trailing dims into the last element +@pure rdims{N}(out::NTuple{N}, inds::Tuple{}, ::Type{Val{N}}) = out +@pure rdims{N}(out::NTuple{N}, inds::Tuple{Any, Vararg{Any}}, ::Type{Val{N}}) = (front(out)..., length(last(out)) * prod(map(length, inds))) +@pure rdims{N}(out::Tuple, inds::Tuple{}, ::Type{Val{N}}) = rdims((out..., OneTo(1)), (), Val{N}) +@pure rdims{N}(out::Tuple, inds::Tuple{Any, Vararg{Any}}, ::Type{Val{N}}) = rdims((out..., first(inds)), tail(inds), Val{N}) function _reshape(parent::AbstractArray, dims::Dims) prod(dims) == length(parent) || throw(DimensionMismatch("parent has $(length(parent)) elements, which is incompatible with size $dims")) @@ -87,8 +83,6 @@ size_strides(out::Tuple) = out @inline size_strides(out, s, sz...) = size_strides((out..., out[end]*s), sz...) size(A::ReshapedArray) = A.dims -size(A::ReshapedArray, d) = d <= ndims(A) ? A.dims[d] : 1 -similar(A::ReshapedArray, eltype::Type) = similar(parent(A), eltype, size(A)) similar(A::ReshapedArray, eltype::Type, dims::Dims) = similar(parent(A), eltype, dims) linearindexing{R<:ReshapedArrayLF}(::Type{R}) = LinearFast() parent(A::ReshapedArray) = A.parent diff --git a/base/serialize.jl b/base/serialize.jl index adb0048310034..057dec4ed4590 100644 --- a/base/serialize.jl +++ b/base/serialize.jl @@ -235,7 +235,7 @@ _trimmedsubarray(A, V, newindexes, index::ViewIndex, indexes...) = _trimmedsubar trimmedindex(P, d, i::Real) = oftype(i, 1) trimmedindex(P, d, i::Colon) = i -trimmedindex(P, d, i::AbstractVector) = oftype(i, 1:length(i)) +trimmedindex(P, d, i::AbstractArray) = oftype(i, reshape(linearindices(i), indices(i))) function serialize{T<:AbstractString}(s::AbstractSerializer, ss::SubString{T}) # avoid saving a copy of the parent string, keeping the type of ss diff --git a/base/sort.jl b/base/sort.jl index 9b2bd7ea4e724..99900be146ef4 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -2,7 +2,7 @@ module Sort -using Base: Order, copymutable, linearindices, allocate_for, shape, linearindexing, viewindexing, LinearFast +using Base: Order, copymutable, linearindices, linearindexing, viewindexing, LinearFast import Base.sort, @@ -447,7 +447,7 @@ function sortperm(v::AbstractVector; by=identity, rev::Bool=false, order::Ordering=Forward) - p = Base.allocate_for(Vector{Int}, v, shape(v, 1)) + p = similar(Vector{Int}, indices(v, 1)) for (i,ind) in zip(eachindex(p), indices(v, 1)) p[i] = ind end @@ -492,7 +492,7 @@ function sort(A::AbstractArray, dim::Integer; else Av = A[:] sort_chunks!(Av, size(A,1), alg, order) - reshape(Av, A) + reshape(Av, indices(A)) end end @@ -507,7 +507,7 @@ end function sortrows(A::AbstractMatrix; kws...) inds = indices(A,1) T = slicetypeof(A, inds, :) - rows = allocate_for(Vector{T}, A, shape(A, 1)) + rows = similar(Vector{T}, indices(A, 1)) for i in inds rows[i] = view(A, i, :) end @@ -518,7 +518,7 @@ end function sortcols(A::AbstractMatrix; kws...) inds = indices(A,2) T = slicetypeof(A, :, inds) - cols = allocate_for(Vector{T}, A, shape(A, 2)) + cols = similar(Vector{T}, indices(A, 2)) for i in inds cols[i] = view(A, :, i) end @@ -532,7 +532,7 @@ function slicetypeof{T,N}(A::AbstractArray{T,N}, i1, i2) SubArray{T,1,typeof(A),typeof(I),fast} end slice_dummy(::Colon) = Colon() -slice_dummy{T}(::UnitRange{T}) = one(T) +slice_dummy{T}(::AbstractUnitRange{T}) = one(T) ## fast clever sorting for floats ## diff --git a/base/sparse/sparse.jl b/base/sparse/sparse.jl index b09d036e6d8b1..7a7a5a82fc905 100644 --- a/base/sparse/sparse.jl +++ b/base/sparse/sparse.jl @@ -2,7 +2,7 @@ module SparseArrays -using Base: ReshapedArray, setindex_shape_check +using Base: ReshapedArray, setindex_shape_check, to_shape using Base.Sort: Forward using Base.LinAlg: AbstractTriangular, PosDefException diff --git a/base/sparse/sparsematrix.jl b/base/sparse/sparsematrix.jl index a2f2391a2d6e5..6e18bf43b08ed 100644 --- a/base/sparse/sparsematrix.jl +++ b/base/sparse/sparsematrix.jl @@ -251,7 +251,7 @@ end similar(S::SparseMatrixCSC, Tv::Type=eltype(S)) = SparseMatrixCSC(S.m, S.n, copy(S.colptr), copy(S.rowval), Array{Tv}(length(S.nzval))) similar{Tv,Ti,TvNew,TiNew}(S::SparseMatrixCSC{Tv,Ti}, ::Type{TvNew}, ::Type{TiNew}) = SparseMatrixCSC(S.m, S.n, convert(Array{TiNew},S.colptr), convert(Array{TiNew}, S.rowval), Array{TvNew}(length(S.nzval))) -similar{Tv}(S::SparseMatrixCSC, ::Type{Tv}, d::Dims) = spzeros(Tv, d...) +@inline similar{Tv}(S::SparseMatrixCSC, ::Type{Tv}, d::Dims) = spzeros(Tv, d...) function convert{Tv,Ti,TvS,TiS}(::Type{SparseMatrixCSC{Tv,Ti}}, S::SparseMatrixCSC{TvS,TiS}) if Tv == TvS && Ti == TiS @@ -1239,7 +1239,8 @@ function spzeros(Tv::Type, Ti::Type, m::Integer, n::Integer) ((m < 0) || (n < 0)) && throw(ArgumentError("invalid Array dimensions")) SparseMatrixCSC(m, n, ones(Ti, n+1), Array{Ti}(0), Array{Tv}(0)) end - +# de-splatting variant +spzeros(Tv::Type, Ti::Type, sz::Tuple{Integer,Integer}) = spzeros(Tv, Ti, sz[1], sz[2]) speye(n::Integer) = speye(Float64, n) speye(T::Type, n::Integer) = speye(T, n, n) @@ -1413,8 +1414,8 @@ end function gen_broadcast_body_sparse(f::Function, is_first_sparse::Bool) F = Expr(:quote, f) quote - Base.Broadcast.check_broadcast_shape(size(B), A_1) - Base.Broadcast.check_broadcast_shape(size(B), A_2) + Base.Broadcast.check_broadcast_shape(indices(B), A_1) + Base.Broadcast.check_broadcast_shape(indices(B), A_2) colptrB = B.colptr; rowvalB = B.rowval; nzvalB = B.nzval colptr1 = A_1.colptr; rowval1 = A_1.rowval; nzval1 = A_1.nzval @@ -1577,8 +1578,8 @@ function gen_broadcast_body_zpreserving(f::Function, is_first_sparse::Bool) op2 = :(val1) end quote - Base.Broadcast.check_broadcast_shape(size(B), $A1) - Base.Broadcast.check_broadcast_shape(size(B), $A2) + Base.Broadcast.check_broadcast_shape(indices(B), $A1) + Base.Broadcast.check_broadcast_shape(indices(B), $A2) nnzB = isempty(B) ? 0 : nnz($A1) * div(B.n, ($A1).n) * div(B.m, ($A1).m) @@ -1647,16 +1648,16 @@ end broadcast{Tv1,Ti1,Tv2,Ti2}(f::Function, A_1::SparseMatrixCSC{Tv1,Ti1}, A_2::SparseMatrixCSC{Tv2,Ti2}) = - broadcast!(f, spzeros(promote_type(Tv1, Tv2), promote_type(Ti1, Ti2), broadcast_shape(A_1, A_2)...), A_1, A_2) + broadcast!(f, spzeros(promote_type(Tv1, Tv2), promote_type(Ti1, Ti2), to_shape(broadcast_shape(A_1, A_2))), A_1, A_2) -broadcast_zpreserving!(args...) = broadcast!(args...) -broadcast_zpreserving(args...) = broadcast(args...) +@inline broadcast_zpreserving!(args...) = broadcast!(args...) +@inline broadcast_zpreserving(args...) = broadcast(args...) broadcast_zpreserving{Tv1,Ti1,Tv2,Ti2}(f::Function, A_1::SparseMatrixCSC{Tv1,Ti1}, A_2::SparseMatrixCSC{Tv2,Ti2}) = - broadcast_zpreserving!(f, spzeros(promote_type(Tv1, Tv2), promote_type(Ti1, Ti2), broadcast_shape(A_1, A_2)...), A_1, A_2) + broadcast_zpreserving!(f, spzeros(promote_type(Tv1, Tv2), promote_type(Ti1, Ti2), to_shape(broadcast_shape(A_1, A_2))), A_1, A_2) broadcast_zpreserving{Tv,Ti}(f::Function, A_1::SparseMatrixCSC{Tv,Ti}, A_2::Union{Array,BitArray,Number}) = - broadcast_zpreserving!(f, spzeros(promote_eltype(A_1, A_2), Ti, broadcast_shape(A_1, A_2)...), A_1, A_2) + broadcast_zpreserving!(f, spzeros(promote_eltype(A_1, A_2), Ti, to_shape(broadcast_shape(A_1, A_2))), A_1, A_2) broadcast_zpreserving{Tv,Ti}(f::Function, A_1::Union{Array,BitArray,Number}, A_2::SparseMatrixCSC{Tv,Ti}) = - broadcast_zpreserving!(f, spzeros(promote_eltype(A_1, A_2), Ti, broadcast_shape(A_1, A_2)...), A_1, A_2) + broadcast_zpreserving!(f, spzeros(promote_eltype(A_1, A_2), Ti, to_shape(broadcast_shape(A_1, A_2))), A_1, A_2) ## Binary arithmetic and boolean operators @@ -1676,7 +1677,7 @@ for (op, pro) in ((+, :eltype_plus), throw(DimensionMismatch("")) end Tv = ($pro)(A_1, A_2) - B = spzeros(Tv, promote_type(Ti1, Ti2), broadcast_shape(A_1, A_2)...) + B = spzeros(Tv, promote_type(Ti1, Ti2), to_shape(broadcast_shape(A_1, A_2))) $body B end @@ -1718,15 +1719,15 @@ end # macro (.^)(A::Array, B::SparseMatrixCSC) = (.^)(A, full(B)) .+{Tv1,Ti1,Tv2,Ti2}(A_1::SparseMatrixCSC{Tv1,Ti1}, A_2::SparseMatrixCSC{Tv2,Ti2}) = - broadcast!(+, spzeros(eltype_plus(A_1, A_2), promote_type(Ti1, Ti2), broadcast_shape(A_1, A_2)...), A_1, A_2) + broadcast!(+, spzeros(eltype_plus(A_1, A_2), promote_type(Ti1, Ti2), to_shape(broadcast_shape(A_1, A_2))), A_1, A_2) function .-{Tva,Tia,Tvb,Tib}(A::SparseMatrixCSC{Tva,Tia}, B::SparseMatrixCSC{Tvb,Tib}) - broadcast!(-, spzeros(eltype_plus(A, B), promote_type(Tia, Tib), broadcast_shape(A, B)...), A, B) + broadcast!(-, spzeros(eltype_plus(A, B), promote_type(Tia, Tib), to_shape(broadcast_shape(A, B))), A, B) end ## element-wise comparison operators returning SparseMatrixCSC ## -.<{Tv1,Ti1,Tv2,Ti2}(A_1::SparseMatrixCSC{Tv1,Ti1}, A_2::SparseMatrixCSC{Tv2,Ti2}) = broadcast!(<, spzeros( Bool, promote_type(Ti1, Ti2), broadcast_shape(A_1, A_2)...), A_1, A_2) -.!={Tv1,Ti1,Tv2,Ti2}(A_1::SparseMatrixCSC{Tv1,Ti1}, A_2::SparseMatrixCSC{Tv2,Ti2}) = broadcast!(!=, spzeros( Bool, promote_type(Ti1, Ti2), broadcast_shape(A_1, A_2)...), A_1, A_2) +.<{Tv1,Ti1,Tv2,Ti2}(A_1::SparseMatrixCSC{Tv1,Ti1}, A_2::SparseMatrixCSC{Tv2,Ti2}) = broadcast!(<, spzeros( Bool, promote_type(Ti1, Ti2), to_shape(broadcast_shape(A_1, A_2))), A_1, A_2) +.!={Tv1,Ti1,Tv2,Ti2}(A_1::SparseMatrixCSC{Tv1,Ti1}, A_2::SparseMatrixCSC{Tv2,Ti2}) = broadcast!(!=, spzeros( Bool, promote_type(Ti1, Ti2), to_shape(broadcast_shape(A_1, A_2))), A_1, A_2) ## full equality function ==(A1::SparseMatrixCSC, A2::SparseMatrixCSC) diff --git a/base/subarray.jl b/base/subarray.jl index adf7807b321ec..e9a4cf1c57765 100644 --- a/base/subarray.jl +++ b/base/subarray.jl @@ -18,7 +18,7 @@ immutable SubArray{T,N,P,I,L} <: AbstractArray{T,N} end # Compute the linear indexability of the indices, and combine it with the linear indexing of the parent function SubArray(parent::AbstractArray, indexes::Tuple, dims::Tuple) - SubArray(linearindexing(viewindexing(indexes), linearindexing(parent)), parent, indexes, convert(Dims, dims)) + SubArray(linearindexing(viewindexing(indexes), linearindexing(parent)), parent, indexes, map(dimlength, dims)) end function SubArray{P, I, N}(::LinearSlow, parent::P, indexes::I, dims::NTuple{N, Int}) SubArray{eltype(P), N, P, I, false}(parent, indexes, dims, 0, 0) @@ -47,6 +47,9 @@ viewindexing(I::Tuple{Vararg{Any}}) = LinearSlow() # Of course, all other array types are slow viewindexing(I::Tuple{AbstractArray, Vararg{Any}}) = LinearSlow() +dimlength(r::Range) = length(r) +dimlength(i::Integer) = Int(i) + # Simple utilities size(V::SubArray) = V.dims length(V::SubArray) = prod(V.dims) @@ -57,7 +60,7 @@ parent(V::SubArray) = V.parent parentindexes(V::SubArray) = V.indexes parent(a::AbstractArray) = a -parentindexes(a::AbstractArray) = ntuple(i->1:size(a,i), ndims(a)) +parentindexes(a::AbstractArray) = ntuple(i->OneTo(size(a,i)), ndims(a)) ## SubArray creation # Drops singleton dimensions (those indexed with a scalar) @@ -76,11 +79,17 @@ function view{N}(A::AbstractArray, I::Vararg{ViewIndex,N}) # TODO: DEPRECATE FOR @boundscheck checkbounds(A, I...) unsafe_view(reshape(A, Val{N}), I...) end + function unsafe_view{T,N}(A::AbstractArray{T,N}, I::Vararg{ViewIndex,N}) @_inline_meta J = to_indexes(I...) SubArray(A, J, map(dimlength, index_shape(A, J...))) end +function unsafe_view{T,N}(V::SubArray{T,N}, I::Vararg{ViewIndex,N}) + @_inline_meta + idxs = reindex(V, V.indexes, to_indexes(I...)) + SubArray(V.parent, idxs, map(dimlength, (index_shape(V.parent, idxs...)))) +end # Re-indexing is the heart of a view, transforming A[i, j][x, y] to A[i[x], j[y]] # @@ -166,12 +175,6 @@ function setindex!(V::FastContiguousSubArray, x, i::Real) V end -function unsafe_view{T,N}(V::SubArray{T,N}, I::Vararg{ViewIndex,N}) - @_inline_meta - idxs = reindex(V, V.indexes, to_indexes(I...)) - SubArray(V.parent, idxs, index_shape(V.parent, idxs...)) -end - linearindexing{T<:FastSubArray}(::Type{T}) = LinearFast() linearindexing{T<:SubArray}(::Type{T}) = LinearSlow() @@ -258,7 +261,9 @@ unsafe_convert{T,N,P,I<:Tuple{Vararg{RangeIndex}}}(::Type{Ptr{T}}, V::SubArray{T pointer(V::FastSubArray, i::Int) = pointer(V.parent, V.offset1 + V.stride1*i) pointer(V::FastContiguousSubArray, i::Int) = pointer(V.parent, V.offset1 + i) -pointer(V::SubArray, i::Int) = pointer(V, smart_ind2sub(shape(V), i)) +pointer(V::SubArray, i::Int) = _pointer(V, i) +_pointer{T}(V::SubArray{T,1}, i::Int) = pointer(V, (i,)) +_pointer(V::SubArray, i::Int) = pointer(V, ind2sub(indices(V), i)) function pointer{T,N,P<:Array,I<:Tuple{Vararg{RangeIndex}}}(V::SubArray{T,N,P,I}, is::Tuple{Vararg{Int}}) index = first_index(V) @@ -273,33 +278,17 @@ end # they are taken from the range/vector # Since bounds-checking is performance-critical and uses # indices, it's worth optimizing these implementations thoroughly -indices(S::SubArray, d::Integer) = 1 <= d <= ndims(S) ? indices(S)[d] : (d > ndims(S) ? (1:1) : error("dimension $d out of range")) -indices(S::SubArray) = (@_inline_meta; _indices(indicesbehavior(parent(S)), S)) -_indices(::IndicesStartAt1, S::SubArray) = (@_inline_meta; map(s->1:s, size(S))) -_indices(::IndicesBehavior, S::SubArray) = (@_inline_meta; _indices((), 1, S, S.indexes...)) -_indices(out::Tuple, dim, S::SubArray) = out -_indices(out::Tuple, dim, S::SubArray, i1, I...) = (@_inline_meta; _indices((out..., 1:length(i1)), dim+1, S, I...)) -_indices(out::Tuple, dim, S::SubArray, ::Real, I...) = (@_inline_meta; _indices(out, dim+1, S, I...)) -_indices(out::Tuple, dim, S::SubArray, ::Colon, I...) = (@_inline_meta; _indices((out..., indices(parent(S), dim)), dim+1, S, I...)) -indices1{T}(S::SubArray{T,0}) = 1:1 -indices1(S::SubArray) = (@_inline_meta; _indices1(indicesbehavior(parent(S)), S)) -_indices1(::IndicesStartAt1, S::SubArray) = 1:S.dims[1] -_indices1(::IndicesBehavior, S::SubArray) = (@_inline_meta; _indices1(S, 1, S.indexes...)) -_indices1(S::SubArray, dim, i1, I...) = (@_inline_meta; 1:length(i1)) +indices(S::SubArray) = (@_inline_meta; _indices_sub(S, 1, S.indexes...)) +_indices_sub(S::SubArray, dim::Int) = () +_indices_sub(S::SubArray, dim::Int, ::Real, I...) = (@_inline_meta; _indices_sub(S, dim+1, I...)) +_indices_sub(S::SubArray, dim::Int, ::Colon, I...) = (@_inline_meta; (indices(parent(S), dim), _indices_sub(S, dim+1, I...)...)) +_indices_sub(S::SubArray, dim::Int, i1::AbstractArray, I...) = (@_inline_meta; (indices(i1)..., _indices_sub(S, dim+1, I...)...)) +indices1(S::SubArray) = (@_inline_meta; _indices1(S, 1, S.indexes...)) +_indices1(S::SubArray, dim) = OneTo(1) _indices1(S::SubArray, dim, i1::Real, I...) = (@_inline_meta; _indices1(S, dim+1, I...)) _indices1(S::SubArray, dim, i1::Colon, I...) = (@_inline_meta; indices(parent(S), dim)) - -# Moreover, incides(S) is fast but indices(S, d) is slower -indicesperformance{T<:SubArray}(::Type{T}) = IndicesSlow1D() - -indicesbehavior(S::SubArray) = _indicesbehavior(indicesbehavior(parent(S)), S) -_indicesbehavior(::IndicesStartAt1, S::SubArray) = IndicesStartAt1() -_indicesbehavior(::IndicesBehavior, S::SubArray) = (@_inline_meta; _indicesbehavior(keepcolon((), S.indexes...), S)) -keepcolon(out) = out -keepcolon(out, ::Colon, I...) = (@_inline_meta; (Colon(),)) -keepcolon(out, i1, I...) = (@_inline_meta; keepcolon(out, I...)) -_indicesbehavior(::Tuple{}, S::SubArray) = IndicesStartAt1() -_indicesbehavior(::Tuple{Colon}, S::SubArray) = indicesbehavior(parent(S)) +_indices1(S::SubArray, dim, i1::AbstractArray, I...) = (@_inline_meta; indices1(i1)) +_indices1{T}(S::SubArray, dim, i1::AbstractArray{T,0}, I...) = (@_inline_meta; _indices1(S, dim+1, I...)) ## Compatability # deprecate? diff --git a/doc/manual/interfaces.rst b/doc/manual/interfaces.rst index 5ede56eb24f8c..b0f0886da6c22 100644 --- a/doc/manual/interfaces.rst +++ b/doc/manual/interfaces.rst @@ -166,9 +166,8 @@ Methods to implement :func:`similar(A, dims::NTuple{Int}) ` ``similar(A, eltype(A), dims)`` Return a mutable array with the same element type and size `dims` :func:`similar(A, ::Type{S}, dims::NTuple{Int}) ` ``Array{S}(dims)`` Return a mutable array with the specified element type and size **Non-traditional indices** **Default definition** **Brief description** -:func:`Base.indicesbehavior(::Type) ` ``Base.IndicesStartAt1()`` Trait with values ``IndicesStartAt1()``, ``IndicesUnitRange()``, ``IndicesList()`` -:func:`indices(A, d) ` ``1:size(A, d)`` Return the range of valid indices along dimension ``d`` -:func:`Base.similar(A, ::Type{S}, inds::NTuple{Ind}) ` ``similar(A, S, map(Base.dimlength, inds))`` Return a mutable array with the specified indices ``inds`` (see below for discussion of ``Ind``) +:func:`indices(A, d) ` ``OneTo(size(A, d))`` Return the ``AbstractUnitRange`` of valid indices along dimension ``d`` +:func:`Base.similar(A, ::Type{S}, inds::NTuple{Ind}) ` ``similar(A, S, map(Base.dimlength, inds))`` Return a mutable array with the specified indices ``inds`` (see below) ===================================================================== ============================================ ======================================================================================= If a type is defined as a subtype of ``AbstractArray``, it inherits a very large set of rich behaviors including iteration and multidimensional indexing built on top of single-element access. See the :ref:`arrays manual page ` and :ref:`standard library section ` for more supported methods. @@ -290,9 +289,9 @@ In addition to all the iterable and indexable methods from above, these types ca If you are defining an array type that allows non-traditional indexing (indices that start at something other than 1), you should specialize -``indices`` and ``indicesbehavior``. You should also specialize -``similar`` so that the ``dims`` argument (ordinarily a ``Dims`` -size-tuple) can be a mixture of ``Integer`` and ``UnitRange`` objects; -the ``Integer`` entries imply that the indexing starts from 1, whereas -the dimensions encoded with ``UnitRange`` may have arbitrary starting -index. +``indices``. You should also specialize ``similar`` so that the +``dims`` argument (ordinarily a ``Dims`` size-tuple) can accept +``AbstractUnitRange`` objects, perhaps range-types ``Ind`` of your own +design. For example, if indexing always starts with 0 for your +arrays, you likely want to define a ``ZeroTo`` range type. Otherwise, +you can use standard ``UnitRange``. diff --git a/doc/stdlib/arrays.rst b/doc/stdlib/arrays.rst index f2deb6bbf98be..960cca00318a7 100644 --- a/doc/stdlib/arrays.rst +++ b/doc/stdlib/arrays.rst @@ -43,18 +43,6 @@ Basic functions Returns the valid range of indices for array ``A`` along dimension ``d``\ . -.. function:: shape(A) - - .. Docstring generated from Julia source - - Returns a tuple specifying the "shape" of array ``A``\ . For arrays with conventional indexing (indices start at 1), this is equivalent to ``size(A)``\ ; otherwise it is equivalent to ``indices(A)``\ . - -.. function:: shape(A, d) - - .. Docstring generated from Julia source - - Specifies the "shape" of the array ``A`` along dimension ``d``\ . For arrays with conventional indexing (starting at 1), this is equivalent to ``size(A, d)``\ ; for arrays with unconventional indexing (indexing may start at something different from 1), it is equivalent to ``indices(A, d)``\ . - .. function:: length(A) -> Integer .. Docstring generated from Julia source @@ -283,31 +271,31 @@ Constructors 2.18425e-314 2.18425e-314 2.18425e-314 2.18425e-314 2.18425e-314 2.18425e-314 2.18425e-314 2.18425e-314 - See also ``allocate_for``\ . - -.. function:: allocate_for(storagetype, referencearray, [shape]) +.. function:: similar(storagetype, indices) .. Docstring generated from Julia source - Create an uninitialized mutable array analogous to that specified by ``storagetype``\ , but with type and shape specified by the final two arguments. The main purpose of this function is to support allocation of arrays that may have unconventional indexing (starting at other than 1), as determined by ``referencearray`` and the optional ``shape`` information. + Create an uninitialized mutable array analogous to that specified by ``storagetype``\ , but with ``indices`` specified by the last argument. ``storagetype`` might be a type or a function. .. code-block:: julia **Examples**: - allocate_for(Array{Int}, A) + similar(Array{Int}, indices(A)) - creates an array that "acts like" an ``Array{Int}`` (and might indeed be backed by one), but which is indexed identically to ``A``\ . If ``A`` has conventional indexing, this will likely just call ``Array{Int}(size(A))``\ , but if ``A`` has unconventional indexing then the indices of the result will match ``A``\ . + creates an array that "acts like" an ``Array{Int}`` (and might indeed be backed by one), but which is indexed identically to ``A``\ . If ``A`` has conventional indexing, this will be identical to ``Array{Int}(size(A))``\ , but if ``A`` has unconventional indexing then the indices of the result will match ``A``\ . .. code-block:: julia - allocate_for(BitArray, A, (shape(A, 2),)) + similar(BitArray, (indices(A, 2),)) would create a 1-dimensional logical array whose indices match those of the columns of ``A``\ . - The main purpose of the ``referencearray`` argument is to select a particular array type supporting unconventional indexing (as it is possible that several different ones will be simultaneously in use). + .. code-block:: julia + + similar(dims->zeros(Int, dims), indices(A)) - See also ``similar``\ . + would create an array of ``Int``\ , initialized to zero, matching the indices of ``A``\ . .. function:: reinterpret(type, A) diff --git a/test/broadcast.jl b/test/broadcast.jl index 23d0b1c0c4245..0cd2dd6a6b45c 100644 --- a/test/broadcast.jl +++ b/test/broadcast.jl @@ -3,7 +3,7 @@ module TestBroadcastInternals using Base.Broadcast: broadcast_shape, check_broadcast_shape, newindex, _bcs, _bcsm -using Base.Test +using Base: Test, OneTo @test @inferred(_bcs((), (3,5), (3,5))) == (3,5) @test @inferred(_bcs((), (3,1), (3,5))) == (3,5) @@ -18,21 +18,21 @@ using Base.Test @test_throws DimensionMismatch _bcs((), (-1:1, 2:6), (-1:1, 2:5)) @test_throws DimensionMismatch _bcs((), (-1:1, 2:5), (2, 2:5)) -@test @inferred(broadcast_shape(zeros(3,4), zeros(3,4))) == (3,4) -@test @inferred(broadcast_shape(zeros(3,4), zeros(3))) == (3,4) -@test @inferred(broadcast_shape(zeros(3), zeros(3,4))) == (3,4) -@test @inferred(broadcast_shape(zeros(3), zeros(1,4), zeros(1))) == (3,4) - -check_broadcast_shape((3,5), zeros(3,5)) -check_broadcast_shape((3,5), zeros(3,1)) -check_broadcast_shape((3,5), zeros(3)) -check_broadcast_shape((3,5), zeros(3,5), zeros(3)) -check_broadcast_shape((3,5), zeros(3,5), 1) -check_broadcast_shape((3,5), 5, 2) -@test_throws DimensionMismatch check_broadcast_shape((3,5), zeros(2,5)) -@test_throws DimensionMismatch check_broadcast_shape((3,5), zeros(3,4)) -@test_throws DimensionMismatch check_broadcast_shape((3,5), zeros(3,4,2)) -@test_throws DimensionMismatch check_broadcast_shape((3,5), zeros(3,5), zeros(2)) +@test @inferred(broadcast_shape(zeros(3,4), zeros(3,4))) == (OneTo(3),OneTo(4)) +@test @inferred(broadcast_shape(zeros(3,4), zeros(3))) == (OneTo(3),OneTo(4)) +@test @inferred(broadcast_shape(zeros(3), zeros(3,4))) == (OneTo(3),OneTo(4)) +@test @inferred(broadcast_shape(zeros(3), zeros(1,4), zeros(1))) == (OneTo(3),OneTo(4)) + +check_broadcast_shape((OneTo(3),OneTo(5)), zeros(3,5)) +check_broadcast_shape((OneTo(3),OneTo(5)), zeros(3,1)) +check_broadcast_shape((OneTo(3),OneTo(5)), zeros(3)) +check_broadcast_shape((OneTo(3),OneTo(5)), zeros(3,5), zeros(3)) +check_broadcast_shape((OneTo(3),OneTo(5)), zeros(3,5), 1) +check_broadcast_shape((OneTo(3),OneTo(5)), 5, 2) +@test_throws DimensionMismatch check_broadcast_shape((OneTo(3),OneTo(5)), zeros(2,5)) +@test_throws DimensionMismatch check_broadcast_shape((OneTo(3),OneTo(5)), zeros(3,4)) +@test_throws DimensionMismatch check_broadcast_shape((OneTo(3),OneTo(5)), zeros(3,4,2)) +@test_throws DimensionMismatch check_broadcast_shape((OneTo(3),OneTo(5)), zeros(3,5), zeros(2)) check_broadcast_shape((-1:1, 6:9), (-1:1, 6:9)) check_broadcast_shape((-1:1, 6:9), (-1:1, 1)) @@ -40,7 +40,6 @@ check_broadcast_shape((-1:1, 6:9), (1, 6:9)) @test_throws DimensionMismatch check_broadcast_shape((-1:1, 6:9), (-1, 6:9)) @test_throws DimensionMismatch check_broadcast_shape((-1:1, 6:9), (-1:1, 6)) check_broadcast_shape((-1:1, 6:9), 1) -check_broadcast_shape((-1:1, 6:9), zeros(1,1)) ci(x) = CartesianIndex(x) @test @inferred(newindex(ci((2,2)), (true, true))) == ci((2,2)) diff --git a/test/choosetests.jl b/test/choosetests.jl index 9818d9acbeb12..9484d0e394f7c 100644 --- a/test/choosetests.jl +++ b/test/choosetests.jl @@ -17,7 +17,7 @@ function choosetests(choices = []) testnames = [ "linalg", "subarray", "core", "inference", "keywordargs", "numbers", "printf", "char", "string", "triplequote", "unicode", - "dates", "dict", "hashing", "iobuffer", "staged", + "dates", "dict", "hashing", "iobuffer", "staged", "offsetarray", "arrayops", "tuple", "reduce", "reducedim", "random", "abstractarray", "intfuncs", "simdloop", "vecelement", "blas", "sparse", "bitarray", "copy", "math", "fastmath", "functional", @@ -33,7 +33,7 @@ function choosetests(choices = []) "markdown", "base64", "serialize", "misc", "threads", "enums", "cmdlineargs", "i18n", "workspace", "libdl", "int", "checked", "intset", "floatfuncs", "compile", "parallel", "inline", - "boundscheck", "error", "ambiguous", "offsetarray", "cartesian" + "boundscheck", "error", "ambiguous", "cartesian" ] if Base.USE_GPL_LIBS diff --git a/test/offsetarray.jl b/test/offsetarray.jl index 790edde60d350..4a91bf6a29f26 100644 --- a/test/offsetarray.jl +++ b/test/offsetarray.jl @@ -7,7 +7,7 @@ module OAs -using Base: SimIdx, Indices, LinearSlow, LinearFast +using Base: DimOrInd, DimsOrInds, Indices, LinearSlow, LinearFast export OffsetArray @@ -24,14 +24,11 @@ OffsetArray{T,N}(A::AbstractArray{T,N}, offsets::Vararg{Int,N}) = OffsetArray(A, (::Type{OffsetArray{T}}){T,N}(inds::Indices{N}) = OffsetArray{T,N}(inds) Base.linearindexing{T<:OffsetArray}(::Type{T}) = Base.linearindexing(parenttype(T)) -Base.indicesbehavior{T<:OffsetArray}(::Type{T}) = Base.IndicesUnitRange() parenttype{T,N,AA}(::Type{OffsetArray{T,N,AA}}) = AA parenttype(A::OffsetArray) = parenttype(typeof(A)) Base.parent(A::OffsetArray) = A.parent Base.size(A::OffsetArray) = size(parent(A)) -Base.eachindex(::LinearSlow, A::OffsetArray) = CartesianRange(indices(A)) -Base.eachindex(::LinearFast, A::OffsetVector) = indices(A, 1) Base.summary(A::OffsetArray) = string(typeof(A))*" with indices "*string(indices(A)) # Implementations of indices and indices1. Since bounds-checking is @@ -48,16 +45,14 @@ Base.indices1{T}(A::OffsetArray{T,0}) = 1:1 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{Vararg{SimIdx}}) +function Base.similar(A::AbstractArray, T::Type, inds::Tuple{UnitRange,Vararg{UnitRange}}) B = similar(A, T, map(Base.dimlength, inds)) OffsetArray(B, map(indsoffset, inds)) end -Base.allocate_for(f, A::OffsetArray, shape::SimIdx) = OffsetArray(f(Base.dimlength(shape)), (indsoffset(shape),)) -Base.allocate_for(f, A::OffsetArray, shape::Tuple{Vararg{SimIdx}}) = OffsetArray(f(map(Base.dimlength, shape)), map(indsoffset, shape)) -Base.promote_indices(a::OffsetArray, b::OffsetArray) = a +Base.similar(f::Union{Function,Type}, shape::Tuple{UnitRange,Vararg{UnitRange}}) = OffsetArray(f(map(Base.dimlength, shape)), map(indsoffset, shape)) -Base.reshape(A::AbstractArray, inds::Indices) = OffsetArray(reshape(A, map(Base.dimlength, inds)), map(indsoffset, inds)) +Base.reshape(A::AbstractArray, inds::Tuple{UnitRange,Vararg{UnitRange}}) = OffsetArray(reshape(A, map(Base.dimlength, inds)), map(indsoffset, inds)) @inline function Base.getindex{T,N}(A::OffsetArray{T,N}, I::Vararg{Int,N}) @boundscheck checkbounds(A, I...) @@ -102,6 +97,7 @@ end using OAs +let # Basics A0 = [1 3; 2 4] A = OffsetArray(A0, (-1,2)) # LinearFast @@ -136,26 +132,30 @@ S = OffsetArray(view(A0, 1:2, 1:2), (-1,2)) # LinearSlow @test eachindex(A) == 1:4 @test eachindex(S) == CartesianRange((0:1,3:4)) -# slice +# view S = view(A, :, 3) @test S == OffsetArray([1,2], (A.offsets[1],)) @test S[0] == 1 @test S[1] == 2 @test_throws BoundsError S[2] +@test indices(S) === (0:1,) S = view(A, 0, :) @test S == OffsetArray([1,3], (A.offsets[2],)) @test S[3] == 1 @test S[4] == 3 @test_throws BoundsError S[1] +@test indices(S) === (3:4,) S = view(A, 0:0, 4) @test S == [3] @test S[1] == 3 @test_throws BoundsError S[0] +@test indices(S) === (Base.OneTo(1),) S = view(A, 1, 3:4) @test S == [2,4] @test S[1] == 2 @test S[2] == 4 @test_throws BoundsError S[3] +@test indices(S) === (Base.OneTo(2),) S = view(A, :, :) @test S == A @test S[0,3] == S[1] == 1 @@ -163,6 +163,7 @@ S = view(A, :, :) @test S[0,4] == S[3] == 3 @test S[1,4] == S[4] == 4 @test_throws BoundsError S[1,1] +@test indices(S) === (0:1, 3:4) # iteration for (a,d) in zip(A, A0) @@ -204,29 +205,33 @@ cmp_showf(Base.print_matrix, io, OffsetArray(rand(10^3,10^3), (10,-9))) # neithe B = similar(A, Float32) @test isa(B, OffsetArray{Float32,2}) @test size(B) == size(A) -@test indices(B) == indices(A) +@test indices(B) === indices(A) B = similar(A, (3,4)) @test isa(B, Array{Int,2}) @test size(B) == (3,4) -@test indices(B) == (1:3, 1:4) -B = similar(A, (-3:3,4)) +@test indices(B) === (Base.OneTo(3), Base.OneTo(4)) +B = similar(A, (-3:3,1:4)) @test isa(B, OffsetArray{Int,2}) -@test indices(B) == (-3:3, 1:4) -B = similar(parent(A), (-3:3,4)) +@test indices(B) === (-3:3, 1:4) +B = similar(parent(A), (-3:3,1:4)) @test isa(B, OffsetArray{Int,2}) -@test indices(B) == (-3:3, 1:4) +@test indices(B) === (-3:3, 1:4) # Indexing with OffsetArray indices i1 = OffsetArray([2,1], (-5,)) i1 = OffsetArray([2,1], -5) b = A0[i1, 1] -@test indices(b) == (-4:-3,) +@test indices(b) === (-4:-3,) @test b[-4] == 2 @test b[-3] == 1 b = A0[1,i1] -@test indices(b) == (-4:-3,) +@test indices(b) === (-4:-3,) @test b[-4] == 3 @test b[-3] == 1 +v = view(A0, i1, 1) +@test indices(v) === (-4:-3,) +v = view(A0, 1:1, i1) +@test indices(v) === (Base.OneTo(1), -4:-3) # logical indexing @test A[A .> 2] == [3,4] @@ -357,3 +362,4 @@ v = OffsetArray(rand(8), (-2,)) @test 2*A == OffsetArray(2*parent(A), A.offsets) @test A+A == OffsetArray(parent(A)+parent(A), A.offsets) @test A.*A == OffsetArray(parent(A).*parent(A), A.offsets) +end diff --git a/test/ranges.jl b/test/ranges.jl index bbe8a4ad931f4..d6c5a0787a95b 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -736,3 +736,33 @@ let r = 1:3, a = [1,2,3] @test convert(Array{Int,1}, r) == a @test convert(Array{Float64,1}, r) == a end + +# OneTo +r = Base.OneTo(-5) +@test isempty(r) +@test length(r) == 0 +@test size(r) == (0,) +r = Base.OneTo(3) +@test !isempty(r) +@test length(r) == 3 +@test size(r) == (3,) +@test step(r) == 1 +@test first(r) == 1 +@test last(r) == 3 +@test minimum(r) == 1 +@test maximum(r) == 3 +@test r[2] == 2 +@test_throws BoundsError r[4] +@test_throws BoundsError r[0] +@test r+1 === 2:4 +@test 2*r === 2:2:6 +k = 0 +for i in r + @test i == (k+=1) +end +@test intersect(r, Base.OneTo(2)) == Base.OneTo(2) +@test intersect(r, 0:5) == 1:3 +io = IOBuffer() +show(io, r) +str = takebuf_string(io) +@test str == "Base.OneTo(3)" diff --git a/test/subarray.jl b/test/subarray.jl index 771e0c963f1e0..5181bf4f6671b 100644 --- a/test/subarray.jl +++ b/test/subarray.jl @@ -1,10 +1,6 @@ # This file is a part of Julia. License is MIT: http://julialang.org/license using Base.Test -# import Base: ViewIndex, dimsizeexpr, rangetype, merge_indexes, first_index, stride1expr, tailsize -using Base.Cartesian - -print_underestimates = false ######## Utilities ########### @@ -336,6 +332,7 @@ sA = view(A, 2:2, 1:5, :) @test parentindexes(sA) == (2:2, 1:5, :) @test Base.parentdims(sA) == [1:3;] @test size(sA) == (1, 5, 8) +@test indices(sA) === (Base.OneTo(1), Base.OneTo(5), Base.OneTo(8)) @test sA[1, 2, 1:8][:] == [5:15:120;] sA[2:5:end] = -1 @test all(sA[2:5:end] .== -1) @@ -355,6 +352,7 @@ test_bounds(sA) sA = view(A, 1:3, 3:3, 2:5) @test Base.parentdims(sA) == [1:3;] @test size(sA) == (3,1,4) +@test indices(sA) === (Base.OneTo(3), Base.OneTo(1), Base.OneTo(4)) @test sA == A[1:3,3:3,2:5] @test sA[:] == A[1:3,3,2:5][:] test_bounds(sA) @@ -367,6 +365,12 @@ sA = view(A, 1:2:3, 1:3:5, 1:2:8) # Test with mixed types @test sA[:, Int16[1,2], big(2)] == [31 40; 33 42] test_bounds(sA) +sA = view(A, 1:1, 1:5, [1 3; 4 2]) +@test ndims(sA) == 4 +@test indices(sA) === (Base.OneTo(1), Base.OneTo(5), Base.OneTo(2), Base.OneTo(2)) +sA = view(A, 1:2, 3, [1 3; 4 2]) +@test ndims(sA) == 3 +@test indices(sA) === (Base.OneTo(2), Base.OneTo(2), Base.OneTo(2)) # sub logical indexing #4763 A = view([1:10;], 5:8) @@ -384,6 +388,7 @@ sA = view(A, 2, :, 1:8) @test parentindexes(sA) == (2, :, 1:8) @test Base.parentdims(sA) == [2:3;] @test size(sA) == (5, 8) +@test indices(sA) === (Base.OneTo(5), Base.OneTo(8)) @test strides(sA) == (3,15) @test sA[2, 1:8][:] == [5:15:120;] @test sA[:,1] == [2:3:14;] @@ -395,11 +400,13 @@ test_bounds(sA) sA = view(A, 1:3, 1:5, 5) @test Base.parentdims(sA) == [1:2;] @test size(sA) == (3,5) +@test indices(sA) === (Base.OneTo(3),Base.OneTo(5)) @test strides(sA) == (1,3) test_bounds(sA) sA = view(A, 1:2:3, 3, 1:2:8) @test Base.parentdims(sA) == [1,3] @test size(sA) == (2,4) +@test indices(sA) === (Base.OneTo(2), Base.OneTo(4)) @test strides(sA) == (2,30) @test sA[:] == A[sA.indexes...][:] test_bounds(sA)