Skip to content

Commit

Permalink
Merge 2dcef54 into a8bca12
Browse files Browse the repository at this point in the history
  • Loading branch information
timholy authored Jul 7, 2016
2 parents a8bca12 + 2dcef54 commit 50c986e
Show file tree
Hide file tree
Showing 23 changed files with 356 additions and 370 deletions.
209 changes: 68 additions & 141 deletions base/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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 ##

Expand All @@ -28,27 +34,19 @@ 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)
function indices{T,N}(A::AbstractArray{T,N})
@_inline_pure_meta
map(s->OneTo(s), size(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))

"""
linearindices(A)
Expand Down Expand Up @@ -144,45 +142,13 @@ 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")
Expand All @@ -203,15 +169,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
Expand Down Expand Up @@ -312,7 +278,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)])
Expand Down Expand Up @@ -348,79 +313,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

Expand Down Expand Up @@ -629,6 +569,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)
Expand Down Expand Up @@ -1255,38 +1196,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...)
Expand All @@ -1300,37 +1230,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}...)
Expand Down Expand Up @@ -1405,7 +1332,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;]

Expand All @@ -1430,7 +1357,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...,))

Expand Down
Loading

0 comments on commit 50c986e

Please sign in to comment.