diff --git a/NEWS.md b/NEWS.md index 3a396acd85a3c3..aa441661f4acd5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -624,8 +624,10 @@ Library improvements other containers, by taking the multiplicity of the arguments into account. Use `unique` to get the old behavior. - * The type `LinearIndices` has been added, providing conversion from - cartesian indices to linear indices using the normal indexing operation. ([#24715]) + * The `linearindices` function has been deprecated in favor of the new + `LinearIndices` type, which additionnally provides conversion from + cartesian indices to linear indices using the normal indexing operation. + ([#24715], [#26775]). * `IdDict{K,V}` replaces `ObjectIdDict`. It has type parameters like other `AbstractDict` subtypes and its constructors mirror the @@ -1446,4 +1448,5 @@ Command-line option changes [#26436]: https://github.com/JuliaLang/julia/issues/26436 [#26442]: https://github.com/JuliaLang/julia/issues/26442 [#26600]: https://github.com/JuliaLang/julia/issues/26600 -[#26670]: https://github.com/JuliaLang/julia/issues/26670 \ No newline at end of file +[#26670]: https://github.com/JuliaLang/julia/issues/26670 +[#26775]: https://github.com/JuliaLang/julia/issues/26775 \ No newline at end of file diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 28aaebb6bffea7..963488070599eb 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -96,33 +96,8 @@ indices1(iter) = OneTo(length(iter)) unsafe_indices(A) = axes(A) unsafe_indices(r::AbstractRange) = (OneTo(unsafe_length(r)),) # Ranges use checked_sub for size -""" - linearindices(A) - -Return a `UnitRange` specifying the valid range of indices for `A[i]` -where `i` is an `Int`. For arrays with conventional indexing (indices -start at 1), or any multidimensional array, this is `1:length(A)`; -however, for one-dimensional arrays with unconventional indices, this -is `axes(A, 1)`. - -Calling this function is the "safe" way to write algorithms that -exploit linear indexing. - -# Examples -```jldoctest -julia> A = fill(1, (5,6,7)); - -julia> b = linearindices(A); - -julia> extrema(b) -(1, 210) -``` -""" -linearindices(A::AbstractArray) = (@_inline_meta; OneTo(_length(A))) -linearindices(A::AbstractVector) = (@_inline_meta; indices1(A)) - keys(a::AbstractArray) = CartesianIndices(axes(a)) -keys(a::AbstractVector) = linearindices(a) +keys(a::AbstractVector) = LinearIndices(a) prevind(::AbstractArray, i::Integer) = Int(i)-1 nextind(::AbstractArray, i::Integer) = Int(i)+1 @@ -187,7 +162,7 @@ julia> lastindex(rand(3,4,5), 2) 4 ``` """ -lastindex(a::AbstractArray) = (@_inline_meta; last(linearindices(a))) +lastindex(a::AbstractArray) = (@_inline_meta; last(LinearIndices(a))) lastindex(a::AbstractArray, d) = (@_inline_meta; last(axes(a, d))) """ @@ -205,7 +180,7 @@ julia> firstindex(rand(3,4,5), 2) 1 ``` """ -firstindex(a::AbstractArray) = (@_inline_meta; first(linearindices(a))) +firstindex(a::AbstractArray) = (@_inline_meta; first(LinearIndices(a))) firstindex(a::AbstractArray, d) = (@_inline_meta; first(axes(a, d))) first(a::AbstractArray) = a[first(eachindex(a))] @@ -321,49 +296,6 @@ function trailingsize(inds::Indices) prod(map(unsafe_length, inds)) end -## Traits for array types ## - -abstract type IndexStyle end -struct IndexLinear <: IndexStyle end -struct IndexCartesian <: IndexStyle end - -""" - IndexStyle(A) - IndexStyle(typeof(A)) - -`IndexStyle` specifies the "native indexing style" for array `A`. When -you define a new `AbstractArray` type, you can choose to implement -either linear indexing or cartesian indexing. If you decide to -implement linear indexing, then you must set this trait for your array -type: - - Base.IndexStyle(::Type{<:MyArray}) = IndexLinear() - -The default is `IndexCartesian()`. - -Julia's internal indexing machinery will automatically (and invisibly) -convert all indexing operations into the preferred style. This allows users -to access elements of your array using any indexing style, even when explicit -methods have not been provided. - -If you define both styles of indexing for your `AbstractArray`, this -trait can be used to select the most performant indexing style. Some -methods check this trait on their inputs, and dispatch to different -algorithms depending on the most efficient access pattern. In -particular, [`eachindex`](@ref) creates an iterator whose type depends -on the setting of this trait. -""" -IndexStyle(A::AbstractArray) = IndexStyle(typeof(A)) -IndexStyle(::Type{Union{}}) = IndexLinear() -IndexStyle(::Type{<:AbstractArray}) = IndexCartesian() -IndexStyle(::Type{<:Array}) = IndexLinear() -IndexStyle(::Type{<:AbstractRange}) = IndexLinear() - -IndexStyle(A::AbstractArray, B::AbstractArray) = IndexStyle(IndexStyle(A), IndexStyle(B)) -IndexStyle(A::AbstractArray, B::AbstractArray...) = IndexStyle(IndexStyle(A), IndexStyle(B...)) -IndexStyle(::IndexLinear, ::IndexLinear) = IndexLinear() -IndexStyle(::IndexStyle, ::IndexStyle) = IndexCartesian() - ## Bounds checking ## # The overall hierarchy is @@ -411,10 +343,11 @@ function checkbounds(::Type{Bool}, A::AbstractArray, I...) @_inline_meta checkbounds_indices(Bool, axes(A), I) end + # Linear indexing is explicitly allowed when there is only one (non-cartesian) index function checkbounds(::Type{Bool}, A::AbstractArray, i) @_inline_meta - checkindex(Bool, linearindices(A), i) + checkindex(Bool, eachindex(IndexLinear(), A), i) end # As a special extension, allow using logical arrays that match the source array exactly function checkbounds(::Type{Bool}, A::AbstractArray{<:Any,N}, I::AbstractArray{Bool,N}) where N @@ -674,7 +607,7 @@ function copyto!(dest::AbstractArray, dstart::Integer, src, sstart::Integer, n:: n < 0 && throw(ArgumentError(string("tried to copy n=", n, " elements, but n should be nonnegative"))) n == 0 && return dest dmax = dstart + n - 1 - inds = linearindices(dest) + inds = LinearIndices(dest) if (dstart ∉ inds || dmax ∉ inds) | (sstart < 1) sstart < 1 && throw(ArgumentError(string("source start offset (",sstart,") is < 1"))) throw(BoundsError(dest, dstart:dmax)) @@ -704,7 +637,7 @@ copyto!(dest::AbstractArray, src::AbstractArray) = copyto!(IndexStyle(dest), dest, IndexStyle(src), src) function copyto!(::IndexStyle, dest::AbstractArray, ::IndexStyle, src::AbstractArray) - destinds, srcinds = linearindices(dest), linearindices(src) + destinds, srcinds = LinearIndices(dest), LinearIndices(src) isempty(srcinds) || (first(srcinds) ∈ destinds && last(srcinds) ∈ destinds) || throw(BoundsError(dest, srcinds)) @inbounds for i in srcinds @@ -714,7 +647,7 @@ function copyto!(::IndexStyle, dest::AbstractArray, ::IndexStyle, src::AbstractA end function copyto!(::IndexStyle, dest::AbstractArray, ::IndexCartesian, src::AbstractArray) - destinds, srcinds = linearindices(dest), linearindices(src) + destinds, srcinds = LinearIndices(dest), LinearIndices(src) isempty(srcinds) || (first(srcinds) ∈ destinds && last(srcinds) ∈ destinds) || throw(BoundsError(dest, srcinds)) i = 0 @@ -725,11 +658,11 @@ function copyto!(::IndexStyle, dest::AbstractArray, ::IndexCartesian, src::Abstr end function copyto!(dest::AbstractArray, dstart::Integer, src::AbstractArray) - copyto!(dest, dstart, src, first(linearindices(src)), _length(src)) + copyto!(dest, dstart, src, first(LinearIndices(src)), _length(src)) end function copyto!(dest::AbstractArray, dstart::Integer, src::AbstractArray, sstart::Integer) - srcinds = linearindices(src) + srcinds = LinearIndices(src) sstart ∈ srcinds || throw(BoundsError(src, sstart)) copyto!(dest, dstart, src, sstart, last(srcinds)-sstart+1) end @@ -739,7 +672,7 @@ function copyto!(dest::AbstractArray, dstart::Integer, n::Integer) n == 0 && return dest n < 0 && throw(ArgumentError(string("tried to copy n=", n, " elements, but n should be nonnegative"))) - destinds, srcinds = linearindices(dest), linearindices(src) + destinds, srcinds = LinearIndices(dest), LinearIndices(src) (dstart ∈ destinds && dstart+n-1 ∈ destinds) || throw(BoundsError(dest, dstart:dstart+n-1)) (sstart ∈ srcinds && sstart+n-1 ∈ srcinds) || throw(BoundsError(src, sstart:sstart+n-1)) @inbounds for i = 0:(n-1) @@ -868,11 +801,13 @@ function eachindex(A::AbstractArray, B::AbstractArray...) @_inline_meta eachindex(IndexStyle(A,B...), A, B...) end -eachindex(::IndexLinear, A::AbstractArray) = linearindices(A) +eachindex(::IndexLinear, A::AbstractArray) = (@_inline_meta; OneTo(_length(A))) +eachindex(::IndexLinear, A::AbstractVector) = (@_inline_meta; indices1(A)) function eachindex(::IndexLinear, A::AbstractArray, B::AbstractArray...) @_inline_meta - indsA = linearindices(A) - _all_match_first(linearindices, indsA, B...) || throw_eachindex_mismatch(IndexLinear(), A, B...) + indsA = eachindex(IndexLinear(), A) + _all_match_first(X->eachindex(IndexLinear(), X), indsA, B...) || + throw_eachindex_mismatch(IndexLinear(), A, B...) indsA end function _all_match_first(f::F, inds, A, B...) where F<:Function @@ -911,7 +846,7 @@ end pointer(x::AbstractArray{T}) where {T} = unsafe_convert(Ptr{T}, x) function pointer(x::AbstractArray{T}, i::Integer) where T @_inline_meta - unsafe_convert(Ptr{T}, x) + (i - first(linearindices(x)))*elsize(x) + unsafe_convert(Ptr{T}, x) + (i - first(LinearIndices(x)))*elsize(x) end ## Approach: @@ -2025,7 +1960,7 @@ end @inline ith_all(i, as) = (as[1][i], ith_all(i, tail(as))...) function map_n!(f::F, dest::AbstractArray, As) where F - for i = linearindices(As[1]) + for i = LinearIndices(As[1]) dest[i] = f(ith_all(i, As)...) end return dest diff --git a/base/array.jl b/base/array.jl index a9c8e46b0de28d..6e706d19d9da9d 100644 --- a/base/array.jl +++ b/base/array.jl @@ -567,7 +567,7 @@ function _collect(c, itr, ::EltypeUnknown, isz::Union{HasLength,HasShape}) end function collect_to_with_first!(dest::AbstractArray, v1, itr, st) - i1 = first(linearindices(dest)) + i1 = first(LinearIndices(dest)) dest[i1] = v1 return collect_to!(dest, itr, i1+1, st) end @@ -1385,15 +1385,15 @@ julia> reverse(A, 3, 5) 3 ``` """ -function reverse(A::AbstractVector, s=first(linearindices(A)), n=last(linearindices(A))) +function reverse(A::AbstractVector, s=first(LinearIndices(A)), n=last(LinearIndices(A))) B = similar(A) - for i = first(linearindices(A)):s-1 + for i = first(LinearIndices(A)):s-1 B[i] = A[i] end for i = s:n B[i] = A[n+s-i] end - for i = n+1:last(linearindices(A)) + for i = n+1:last(LinearIndices(A)) B[i] = A[i] end return B @@ -1403,7 +1403,7 @@ end reverse(A::Vector) = invoke(reverse, Tuple{AbstractVector}, A) function reverseind(a::AbstractVector, i::Integer) - li = linearindices(a) + li = LinearIndices(a) first(li) + last(li) - i end @@ -1433,8 +1433,8 @@ julia> A 1 ``` """ -function reverse!(v::AbstractVector, s=first(linearindices(v)), n=last(linearindices(v))) - liv = linearindices(v) +function reverse!(v::AbstractVector, s=first(LinearIndices(v)), n=last(LinearIndices(v))) + liv = LinearIndices(v) if n <= s # empty case; ok elseif !(first(liv) ≤ s ≤ last(liv)) throw(BoundsError(v, s)) diff --git a/base/broadcast.jl b/base/broadcast.jl index d5e0dca95315c2..390c6316cd81da 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -3,7 +3,7 @@ module Broadcast using .Base.Cartesian -using .Base: Indices, OneTo, linearindices, tail, to_shape, +using .Base: Indices, OneTo, tail, to_shape, _msk_end, unsafe_bitgetindex, bitcache_chunks, bitcache_size, dumpbitcache, isoperator, promote_typejoin, unalias import .Base: broadcast, broadcast! diff --git a/base/deprecated.jl b/base/deprecated.jl index 2030184ade8afe..baf0936c78aede 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1580,6 +1580,8 @@ function search(buf::IOBuffer, delim::UInt8) return Int(q-p+1) end +@deprecate linearindices(x::AbstractArray) LinearIndices(x) + # PR #26647 # The `keep` argument in `split` and `rpslit` has been renamed to `keepempty`. # To remove this deprecation, remove the `keep` argument from the function signatures as well as diff --git a/base/errorshow.jl b/base/errorshow.jl index fb260ffb0e99c2..cdaaa940f1ed8e 100644 --- a/base/errorshow.jl +++ b/base/errorshow.jl @@ -553,7 +553,7 @@ function is_default_method(m::Method) end @noinline function throw_eachindex_mismatch(::IndexLinear, A...) - throw(DimensionMismatch("all inputs to eachindex must have the same indices, got $(join(linearindices.(A), ", ", " and "))")) + throw(DimensionMismatch("all inputs to eachindex must have the same indices, got $(join(LinearIndices.(A), ", ", " and "))")) end @noinline function throw_eachindex_mismatch(::IndexCartesian, A...) throw(DimensionMismatch("all inputs to eachindex must have the same axes, got $(join(axes.(A), ", ", " and "))")) diff --git a/base/essentials.jl b/base/essentials.jl index 04f8ec8ef9b84e..e894826ce7dfc5 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -535,7 +535,6 @@ next(v::SimpleVector,i) = (v[i],i+1) done(v::SimpleVector,i) = (length(v) < i) isempty(v::SimpleVector) = (length(v) == 0) axes(v::SimpleVector) = (OneTo(length(v)),) -linearindices(v::SimpleVector) = axes(v, 1) axes(v::SimpleVector, d) = d <= 1 ? axes(v)[d] : OneTo(1) function ==(v1::SimpleVector, v2::SimpleVector) diff --git a/base/exports.jl b/base/exports.jl index d4d4b5f05a5795..5fe3f330537e16 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -392,7 +392,6 @@ export isperm, issorted, last, - linearindices, mapslices, max, maximum!, diff --git a/base/indices.jl b/base/indices.jl index e05d489380a6dc..92e4323ed76ff1 100644 --- a/base/indices.jl +++ b/base/indices.jl @@ -4,6 +4,49 @@ Dims{N} = NTuple{N,Int} DimsInteger{N} = NTuple{N,Integer} Indices{N} = NTuple{N,AbstractUnitRange} +## Traits for array types ## + +abstract type IndexStyle end +struct IndexLinear <: IndexStyle end +struct IndexCartesian <: IndexStyle end + +""" + IndexStyle(A) + IndexStyle(typeof(A)) + +`IndexStyle` specifies the "native indexing style" for array `A`. When +you define a new `AbstractArray` type, you can choose to implement +either linear indexing or cartesian indexing. If you decide to +implement linear indexing, then you must set this trait for your array +type: + + Base.IndexStyle(::Type{<:MyArray}) = IndexLinear() + +The default is `IndexCartesian()`. + +Julia's internal indexing machinery will automatically (and invisibly) +convert all indexing operations into the preferred style. This allows users +to access elements of your array using any indexing style, even when explicit +methods have not been provided. + +If you define both styles of indexing for your `AbstractArray`, this +trait can be used to select the most performant indexing style. Some +methods check this trait on their inputs, and dispatch to different +algorithms depending on the most efficient access pattern. In +particular, [`eachindex`](@ref) creates an iterator whose type depends +on the setting of this trait. +""" +IndexStyle(A::AbstractArray) = IndexStyle(typeof(A)) +IndexStyle(::Type{Union{}}) = IndexLinear() +IndexStyle(::Type{<:AbstractArray}) = IndexCartesian() +IndexStyle(::Type{<:Array}) = IndexLinear() +IndexStyle(::Type{<:AbstractRange}) = IndexLinear() + +IndexStyle(A::AbstractArray, B::AbstractArray) = IndexStyle(IndexStyle(A), IndexStyle(B)) +IndexStyle(A::AbstractArray, B::AbstractArray...) = IndexStyle(IndexStyle(A), IndexStyle(B...)) +IndexStyle(::IndexLinear, ::IndexLinear) = IndexLinear() +IndexStyle(::IndexStyle, ::IndexStyle) = IndexCartesian() + # array shape rules promote_shape(::Tuple{}, ::Tuple{}) = () @@ -213,7 +256,7 @@ given tuple of indices and the dimensional indices of `A` in tandem. As such, not all index types are guaranteed to propagate to `Base.to_index`. """ to_indices(A, I::Tuple) = (@_inline_meta; to_indices(A, axes(A), I)) -to_indices(A, I::Tuple{Any}) = (@_inline_meta; to_indices(A, (linearindices(A),), I)) +to_indices(A, I::Tuple{Any}) = (@_inline_meta; to_indices(A, (eachindex(IndexLinear(), A),), I)) to_indices(A, inds, ::Tuple{}) = () to_indices(A, inds, I::Tuple{Any, Vararg{Any}}) = (@_inline_meta; (to_index(A, I[1]), to_indices(A, _maybetail(inds), tail(I))...)) @@ -249,3 +292,78 @@ show(io::IO, r::Slice) = print(io, "Base.Slice(", r.indices, ")") start(S::Slice) = start(S.indices) next(S::Slice, s) = next(S.indices, s) done(S::Slice, s) = done(S.indices, s) + +""" + LinearIndices(A::AbstractArray) + +Return a `LinearIndices` array with the same shape and [`axes`](@ref) as `A`, +holding the linear index of each entry in `A`. Indexing this array with +cartesian indices allows mapping them to linear indices. + +For arrays with conventional indexing (indices start at 1), or any multidimensional +array, linear indices range from 1 to `length(A)`. However, for `AbstractVector`s +linear indices are `axes(A, 1)`, and therefore do not start at 1 for vectors with +unconventional indexing. + +Calling this function is the "safe" way to write algorithms that +exploit linear indexing. + +# Examples +```jldoctest +julia> A = fill(1, (5,6,7)); + +julia> b = LinearIndices(A); + +julia> extrema(b) +(1, 210) +``` + + LinearIndices(inds::CartesianIndices) -> R + LinearIndices(sz::Dims) -> R + LinearIndices(istart:istop, jstart:jstop, ...) -> R + +Return a `LinearIndices` array with the specified shape or [`axes`](@ref). + +# Example + +The main purpose of this constructor is intuitive conversion +from cartesian to linear indexing: + +```jldoctest +julia> linear = LinearIndices((1:3, 1:2)) +LinearIndices{2,Tuple{UnitRange{Int64},UnitRange{Int64}}} with indices 1:3×1:2: + 1 4 + 2 5 + 3 6 + +julia> linear[1,2] +4 +``` +""" +struct LinearIndices{N,R<:NTuple{N,AbstractUnitRange{Int}}} <: AbstractArray{Int,N} + indices::R +end + +LinearIndices(::Tuple{}) = LinearIndices{0,typeof(())}(()) +LinearIndices(inds::NTuple{N,AbstractUnitRange{Int}}) where {N} = LinearIndices{N,typeof(inds)}(inds) +LinearIndices(inds::NTuple{N,AbstractUnitRange{<:Integer}}) where {N} = + LinearIndices(map(r->convert(AbstractUnitRange{Int}, r), inds)) +LinearIndices(sz::NTuple{N,<:Integer}) where {N} = LinearIndices(map(Base.OneTo, sz)) +LinearIndices(inds::NTuple{N,Union{<:Integer,AbstractUnitRange{<:Integer}}}) where {N} = + LinearIndices(map(i->first(i):last(i), inds)) +LinearIndices(A::Union{AbstractArray,SimpleVector}) = LinearIndices(axes(A)) + +# AbstractArray implementation +IndexStyle(::Type{LinearIndices{N,R}}) where {N,R} = IndexLinear() +axes(iter::LinearIndices{N,R}) where {N,R} = iter.indices +size(iter::LinearIndices{N,R}) where {N,R} = length.(iter.indices) +function getindex(iter::LinearIndices{N,R}, i::Int) where {N,R} + @boundscheck checkbounds(iter, i) + i +end + +# Needed since firstindex and lastindex are defined in terms of LinearIndices +first(iter::LinearIndices) = 1 +first(iter::LinearIndices{1}) = (@_inline_meta; first(iter.indices[1])) +last(iter::LinearIndices) = (@_inline_meta; length(iter)) +last(iter::LinearIndices{1}) = (@_inline_meta; last(iter.indices[1])) \ No newline at end of file diff --git a/base/iterators.jl b/base/iterators.jl index 8f5f91e7a7137d..07e9e3a5b68ab9 100644 --- a/base/iterators.jl +++ b/base/iterators.jl @@ -12,7 +12,7 @@ using .Base: @inline, Pair, AbstractDict, IndexLinear, IndexCartesian, IndexStyle, AbstractVector, Vector, tail, tuple_type_head, tuple_type_tail, tuple_type_cons, SizeUnknown, HasLength, HasShape, IsInfinite, EltypeUnknown, HasEltype, OneTo, @propagate_inbounds, Generator, AbstractRange, - linearindices, (:), |, +, -, !==, !, <=, < + LinearIndices, (:), |, +, -, !==, !, <=, < import .Base: start, done, next, first, last, @@ -211,7 +211,7 @@ CartesianIndex(2, 2) e See also: [`IndexStyle`](@ref), [`axes`](@ref). """ -pairs(::IndexLinear, A::AbstractArray) = Pairs(A, linearindices(A)) +pairs(::IndexLinear, A::AbstractArray) = Pairs(A, LinearIndices(A)) pairs(::IndexCartesian, A::AbstractArray) = Pairs(A, CartesianIndices(axes(A))) # preserve indexing capabilities for known indexable types diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 8c8997e0534352..c21b591f972744 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -11,7 +11,7 @@ module IteratorsMD using .Base: IndexLinear, IndexCartesian, AbstractCartesianIndex, fill_to_length, tail using .Base.Iterators: Reverse - export CartesianIndex, CartesianIndices, LinearIndices + export CartesianIndex, CartesianIndices """ CartesianIndex(i, j, k...) -> I @@ -377,49 +377,7 @@ module IteratorsMD next(iter::Reverse{<:CartesianIndices{0}}, state) = CartesianIndex(), true done(iter::Reverse{<:CartesianIndices{0}}, state) = state - """ - LinearIndices(inds::CartesianIndices) -> R - LinearIndices(sz::Dims) -> R - LinearIndices(istart:istop, jstart:jstop, ...) -> R - - Define a mapping between cartesian indices and the corresponding linear index into a `CartesianIndices`. - - # Example - - The main purpose of this type is intuitive conversion from cartesian to linear indexing: - - ```jldoctest - julia> linear = LinearIndices((1:3, 1:2)) - LinearIndices{2,Tuple{UnitRange{Int64},UnitRange{Int64}}} with indices 1:3×1:2: - 1 4 - 2 5 - 3 6 - - julia> linear[1,2] - 4 - ``` - """ - struct LinearIndices{N,R<:NTuple{N,AbstractUnitRange{Int}}} <: AbstractArray{Int,N} - indices::R - end - LinearIndices(inds::CartesianIndices{N,R}) where {N,R} = LinearIndices{N,R}(inds.indices) - LinearIndices(::Tuple{}) = LinearIndices(CartesianIndices(())) - LinearIndices(inds::NTuple{N,AbstractUnitRange{Int}}) where {N} = LinearIndices(CartesianIndices(inds)) - LinearIndices(inds::NTuple{N,AbstractUnitRange{<:Integer}}) where {N} = LinearIndices(CartesianIndices(inds)) - LinearIndices(index::CartesianIndex) = LinearIndices(CartesianIndices(index)) - LinearIndices(sz::NTuple{N,<:Integer}) where {N} = LinearIndices(CartesianIndices(sz)) - LinearIndices(inds::NTuple{N,Union{<:Integer,AbstractUnitRange{<:Integer}}}) where {N} = LinearIndices(CartesianIndices(inds)) - LinearIndices(A::AbstractArray) = LinearIndices(CartesianIndices(A)) - - # AbstractArray implementation - Base.IndexStyle(::Type{LinearIndices{N,R}}) where {N,R} = IndexLinear() - Base.axes(iter::LinearIndices{N,R}) where {N,R} = iter.indices - Base.size(iter::LinearIndices{N,R}) where {N,R} = length.(iter.indices) - function Base.getindex(iter::LinearIndices{N,R}, i::Int) where {N,R} - @boundscheck checkbounds(iter, i) - i - end end # IteratorsMD @@ -536,7 +494,7 @@ show(io::IO, r::LogicalIndex) = print(io, "Base.LogicalIndex(", r.mask, ")") # keep track of the count of elements since we already know how many there # should be -- this way we don't need to look at future indices to check done. @inline function start(L::LogicalIndex{Int}) - r = linearindices(L.mask) + r = LinearIndices(L.mask) return (r, start(r), 1) end @inline function start(L::LogicalIndex{<:CartesianIndex}) @@ -570,7 +528,7 @@ end @inline done(L::LogicalIndex{Int,<:BitArray}, s) = s[2] > length(L) @inline checkbounds(::Type{Bool}, A::AbstractArray, I::LogicalIndex{<:Any,<:AbstractArray{Bool,1}}) = - linearindices(A) == linearindices(I.mask) + eachindex(IndexLinear(), A) == eachindex(IndexLinear(), I.mask) @inline checkbounds(::Type{Bool}, A::AbstractArray, I::LogicalIndex) = axes(A) == axes(I.mask) @inline checkindex(::Type{Bool}, indx::AbstractUnitRange, I::LogicalIndex) = (indx,) == axes(I.mask) checkindex(::Type{Bool}, inds::Tuple, I::LogicalIndex) = false @@ -711,8 +669,8 @@ function _accumulate_pairwise!(op::Op, c::AbstractVector{T}, v::AbstractVector, end function accumulate_pairwise!(op::Op, result::AbstractVector, v::AbstractVector) where Op - li = linearindices(v) - li != linearindices(result) && throw(DimensionMismatch("input and output array sizes and indices must match")) + li = LinearIndices(v) + li != LinearIndices(result) && throw(DimensionMismatch("input and output array sizes and indices must match")) n = length(li) n == 0 && return result i1 = first(li) @@ -1066,8 +1024,8 @@ end function _accumulate1!(op, B, v1, A::AbstractVector, dim::Integer) dim > 0 || throw(ArgumentError("dim must be a positive integer")) - inds = linearindices(A) - inds == linearindices(B) || throw(DimensionMismatch("linearindices of A and B don't match")) + inds = LinearIndices(A) + inds == LinearIndices(B) || throw(DimensionMismatch("LinearIndices of A and B don't match")) dim > 1 && return copyto!(B, A) i1 = inds[1] cur_val = v1 diff --git a/base/range.jl b/base/range.jl index 950e44e300a559..7b52c61f1b9dd6 100644 --- a/base/range.jl +++ b/base/range.jl @@ -557,7 +557,7 @@ function getindex(r::StepRangeLen{T}, s::OrdinalRange{<:Integer}) where {T} @_inline_meta @boundscheck checkbounds(r, s) # Find closest approach to offset by s - ind = linearindices(s) + ind = LinearIndices(s) offset = max(min(1 + round(Int, (r.offset - first(s))/step(s)), last(ind)), first(ind)) ref = _getindex_hiprec(r, first(s) + (offset-1)*step(s)) return StepRangeLen{T}(ref, r.step*step(s), length(s), offset) diff --git a/base/reduce.jl b/base/reduce.jl index 195c0adc850094..1da4a056a281a2 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -331,7 +331,7 @@ mapreduce_first(f, op, x) = reduce_first(op, f(x)) _mapreduce(f, op, A::AbstractArray) = _mapreduce(f, op, IndexStyle(A), A) function _mapreduce(f, op, ::IndexLinear, A::AbstractArray{T}) where T - inds = linearindices(A) + inds = LinearIndices(A) n = length(inds) if n == 0 return mapreduce_empty(f, op, T) diff --git a/base/reducedim.jl b/base/reducedim.jl index 89b01379f2f09e..85b72652718547 100644 --- a/base/reducedim.jl +++ b/base/reducedim.jl @@ -210,7 +210,7 @@ function _mapreducedim!(f, op, R::AbstractArray, A::AbstractArray) if has_fast_linear_indexing(A) && lsiz > 16 # use mapreduce_impl, which is probably better tuned to achieve higher performance nslices = div(_length(A), lsiz) - ibase = first(linearindices(A))-1 + ibase = first(LinearIndices(A))-1 for i = 1:nslices @inbounds R[i] = op(R[i], mapreduce_impl(f, op, A, ibase+1, ibase+lsiz)) ibase += lsiz @@ -658,7 +658,7 @@ end ##### findmin & findmax ##### # The initial values of Rval are not used if the correponding indices in Rind are 0. # -function findminmax!(f, Rval, Rind, A::AbstractArray{T,N}) where {T,N} +function Base.findminmax!(f, Rval, Rind, A::AbstractArray{T,N}) where {T,N} (isempty(Rval) || isempty(A)) && return Rval, Rind lsiz = check_reducedims(Rval, A) for i = 1:N @@ -669,7 +669,7 @@ function findminmax!(f, Rval, Rind, A::AbstractArray{T,N}) where {T,N} indsAt, indsRt = safe_tail(axes(A)), safe_tail(axes(Rval)) keep, Idefault = Broadcast.shapeindexer(indsAt, indsRt) ks = keys(A) - k, kss = next(ks, start(ks)) + kss = start(ks) zi = zero(eltype(ks)) if reducedim1(Rval, A) i1 = first(indices1(Rval)) @@ -678,12 +678,12 @@ function findminmax!(f, Rval, Rind, A::AbstractArray{T,N}) where {T,N} tmpRv = Rval[i1,IR] tmpRi = Rind[i1,IR] for i in axes(A,1) + k, kss = next(ks, kss) tmpAv = A[i,IA] if tmpRi == zi || (tmpRv == tmpRv && (tmpAv != tmpAv || f(tmpAv, tmpRv))) tmpRv = tmpAv tmpRi = k end - k, kss = next(ks, kss) end Rval[i1,IR] = tmpRv Rind[i1,IR] = tmpRi @@ -692,6 +692,7 @@ function findminmax!(f, Rval, Rind, A::AbstractArray{T,N}) where {T,N} @inbounds for IA in CartesianIndices(indsAt) IR = Broadcast.newindex(IA, keep, Idefault) for i in axes(A, 1) + k, kss = next(ks, kss) tmpAv = A[i,IA] tmpRv = Rval[i,IR] tmpRi = Rind[i,IR] @@ -699,7 +700,6 @@ function findminmax!(f, Rval, Rind, A::AbstractArray{T,N}) where {T,N} Rval[i,IR] = tmpAv Rind[i,IR] = k end - k, kss = next(ks, kss) end end end diff --git a/base/show.jl b/base/show.jl index e34a9290fbf7ff..9d875f1c9bfe95 100644 --- a/base/show.jl +++ b/base/show.jl @@ -644,7 +644,7 @@ function show(io::IO, src::CodeInfo) end function show_delim_array(io::IO, itr::Union{AbstractArray,SimpleVector}, op, delim, cl, - delim_one, i1=first(linearindices(itr)), l=last(linearindices(itr))) + delim_one, i1=first(LinearIndices(itr)), l=last(LinearIndices(itr))) print(io, op) if !show_circular(io, itr) recur_io = IOContext(io, :SHOWN_SET => itr) diff --git a/base/sort.jl b/base/sort.jl index 586ec4dedbf534..4d398f434804fb 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -5,7 +5,7 @@ module Sort import ..@__MODULE__, ..parentmodule const Base = parentmodule(@__MODULE__) using .Base.Order -using .Base: copymutable, linearindices, IndexStyle, viewindexing, IndexLinear, _length, (:), +using .Base: copymutable, LinearIndices, IndexStyle, viewindexing, IndexLinear, _length, (:), eachindex, axes, first, last, similar, start, next, done, zip, @views, OrdinalRange, AbstractVector, @inbounds, AbstractRange, @eval, @inline, Vector, @noinline, AbstractMatrix, AbstractUnitRange, isless, identity, eltype, >, <, <=, >=, |, +, -, *, !, @@ -899,7 +899,7 @@ function sort(A::AbstractArray; end @noinline function sort_chunks!(Av, n, alg, order) - inds = linearindices(Av) + inds = LinearIndices(Av) for s = first(inds):n:last(inds) sort!(Av, s, s+n-1, alg, order) end diff --git a/base/statistics.jl b/base/statistics.jl index 3b0bbb5b9f9ac9..9e1b83d4d50b51 100644 --- a/base/statistics.jl +++ b/base/statistics.jl @@ -137,7 +137,7 @@ function centralize_sumabs2!(R::AbstractArray{S}, A::AbstractArray, means::Abstr if has_fast_linear_indexing(A) && lsiz > 16 nslices = div(_length(A), lsiz) - ibase = first(linearindices(A))-1 + ibase = first(LinearIndices(A))-1 for i = 1:nslices @inbounds R[i] = centralize_sumabs2(A, means[i], ibase+1, ibase+lsiz) ibase += lsiz diff --git a/base/subarray.jl b/base/subarray.jl index c43f2dedcdd18d..206abb46e15619 100644 --- a/base/subarray.jl +++ b/base/subarray.jl @@ -86,7 +86,7 @@ end # Transform indices to be "dense" _trimmedindex(i::Real) = oftype(i, 1) _trimmedindex(i::AbstractUnitRange) = oftype(i, OneTo(length(i))) -_trimmedindex(i::AbstractArray) = oftype(i, reshape(linearindices(i), axes(i))) +_trimmedindex(i::AbstractArray) = oftype(i, reshape(eachindex(IndexLinear(), i), axes(i))) ## SubArray creation # We always assume that the dimensionality of the parent matches the number of diff --git a/doc/src/base/arrays.md b/doc/src/base/arrays.md index 3357292838d57f..c2b16d76927c4b 100644 --- a/doc/src/base/arrays.md +++ b/doc/src/base/arrays.md @@ -43,7 +43,7 @@ Base.axes(::Any) Base.axes(::AbstractArray, ::Any) Base.length(::AbstractArray) Base.eachindex -Base.linearindices +Base.LinearIndices Base.IndexStyle Base.conj! Base.stride diff --git a/doc/src/devdocs/offset-arrays.md b/doc/src/devdocs/offset-arrays.md index 97328a6c70886c..9bca679923a6fc 100644 --- a/doc/src/devdocs/offset-arrays.md +++ b/doc/src/devdocs/offset-arrays.md @@ -18,8 +18,8 @@ the exported interfaces of Julia. As an overview, the steps are: * replace many uses of `size` with `axes` - * replace `1:length(A)` with `eachindex(A)`, or in some cases `linearindices(A)` - * replace `length(A)` with `length(linearindices(A))` + * replace `1:length(A)` with `eachindex(A)`, or in some cases `LinearIndices(A)` + * replace `length(A)` with `length(LinearIndices(A))` * replace explicit allocations like `Array{Int}(size(B))` with `similar(Array{Int}, axes(B))` These are described in more detail below. @@ -74,21 +74,21 @@ at the top of any function. For bounds checking, note that there are dedicated functions `checkbounds` and `checkindex` which can sometimes simplify such tests. -### Linear indexing (`linearindices`) +### Linear indexing (`LinearIndices`) Some algorithms are most conveniently (or efficiently) written in terms of a single linear index, `A[i]` even if `A` is multi-dimensional. Regardless of the array's native indices, linear indices always range from `1:length(A)`. However, this raises an ambiguity for one-dimensional arrays (a.k.a., [`AbstractVector`](@ref)): does `v[i]` mean linear indexing , or Cartesian indexing with the array's native indices? -For this reason, your best option may be to iterate over the array with `eachindex(A)`, or, if you require the indices to be sequential integers, to get the index range by calling `linearindices(A)`. This will return `axes(A, 1)` if A is an AbstractVector, and the equivalent of `1:length(A)` otherwise. +For this reason, your best option may be to iterate over the array with `eachindex(A)`, or, if you require the indices to be sequential integers, to get the index range by calling `LinearIndices(A)`. This will return `axes(A, 1)` if A is an AbstractVector, and the equivalent of `1:length(A)` otherwise. By this definition, 1-dimensional arrays always use Cartesian indexing with the array's native indices. To help enforce this, it's worth noting that the index conversion functions will throw an error if shape indicates a 1-dimensional array with unconventional indexing (i.e., is a `Tuple{UnitRange}` rather than a tuple of `OneTo`). For arrays with conventional indexing, these functions continue to work the same as always. -Using `axes` and `linearindices`, here is one way you could rewrite `mycopy!`: +Using `axes` and `LinearIndices`, here is one way you could rewrite `mycopy!`: ```julia function mycopy!(dest::AbstractVector, src::AbstractVector) axes(dest) == axes(src) || throw(DimensionMismatch("vectors must match")) - for i in linearindices(src) + for i in LinearIndices(src) @inbounds dest[i] = src[i] end dest diff --git a/doc/src/manual/performance-tips.md b/doc/src/manual/performance-tips.md index bcd5abeb8c4d7e..ff8ecd570ef485 100644 --- a/doc/src/manual/performance-tips.md +++ b/doc/src/manual/performance-tips.md @@ -1170,7 +1170,7 @@ Sometimes you can enable better optimization by promising certain program proper and could change or disappear in future versions of Julia. The common idiom of using 1:n to index into an AbstractArray is not safe if the Array uses unconventional indexing, -and may cause a segmentation fault if bounds checking is turned off. Use `linearindices(x)` or `eachindex(x)` +and may cause a segmentation fault if bounds checking is turned off. Use `LinearIndices(x)` or `eachindex(x)` instead (see also [offset-arrays](https://docs.julialang.org/en/latest/devdocs/offset-arrays)). Note: While `@simd` needs to be placed directly in front of a loop, both `@inbounds` and `@fastmath` diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index 5e8fd1ac1c5179..13acddbb1ffc05 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -20,7 +20,7 @@ import Base: USE_BLAS64, abs, acos, acosh, acot, acoth, acsc, acsch, adjoint, as using Base: hvcat_fill, iszero, IndexLinear, _length, promote_op, promote_typeof, @propagate_inbounds, @pure, reduce, typed_vcat # We use `_length` because of non-1 indices; releases after julia 0.5 -# can go back to `length`. `_length(A)` is equivalent to `length(linearindices(A))`. +# can go back to `length`. `_length(A)` is equivalent to `length(LinearIndices(A))`. export # Modules diff --git a/stdlib/LinearAlgebra/src/generic.jl b/stdlib/LinearAlgebra/src/generic.jl index 4fcde3190ec76b..0409a950497bc9 100644 --- a/stdlib/LinearAlgebra/src/generic.jl +++ b/stdlib/LinearAlgebra/src/generic.jl @@ -1167,9 +1167,9 @@ end function axpy!(α, x::AbstractArray, rx::AbstractArray{<:Integer}, y::AbstractArray, ry::AbstractArray{<:Integer}) if _length(rx) != _length(ry) throw(DimensionMismatch("rx has length $(_length(rx)), but ry has length $(_length(ry))")) - elseif !checkindex(Bool, linearindices(x), rx) + elseif !checkindex(Bool, eachindex(IndexLinear(), x), rx) throw(BoundsError(x, rx)) - elseif !checkindex(Bool, linearindices(y), ry) + elseif !checkindex(Bool, eachindex(IndexLinear(), y), ry) throw(BoundsError(y, ry)) end for (IY, IX) in zip(eachindex(ry), eachindex(rx)) diff --git a/stdlib/Random/src/RNGs.jl b/stdlib/Random/src/RNGs.jl index f4e4fda97210d4..76325f199eecd9 100644 --- a/stdlib/Random/src/RNGs.jl +++ b/stdlib/Random/src/RNGs.jl @@ -335,7 +335,7 @@ rand(r::MersenneTwister, T::SamplerUnion(Union{Bool,Int8,UInt8,Int16,UInt16,Int3 function rand!(r::MersenneTwister, A::AbstractArray{Float64}, I::SamplerTrivial{<:FloatInterval_64}) - region = linearindices(A) + region = LinearIndices(A) # what follows is equivalent to this simple loop but more efficient: # for i=region # @inbounds A[i] = rand(r, I[]) diff --git a/stdlib/Random/src/generation.jl b/stdlib/Random/src/generation.jl index 4d472db2ee63eb..7e5c3012f5ef2b 100644 --- a/stdlib/Random/src/generation.jl +++ b/stdlib/Random/src/generation.jl @@ -340,7 +340,7 @@ end ## random values from AbstractArray Sampler(rng::AbstractRNG, r::AbstractArray, n::Repetition) = - SamplerSimple(r, Sampler(rng, linearindices(r), n)) + SamplerSimple(r, Sampler(rng, firstindex(r):lastindex(r), n)) rand(rng::AbstractRNG, sp::SamplerSimple{<:AbstractArray,<:Sampler}) = @inbounds return sp[][rand(rng, sp.data)] @@ -352,7 +352,7 @@ function Sampler(rng::AbstractRNG, t::Dict, ::Repetition) isempty(t) && throw(ArgumentError("collection must be non-empty")) # we use Val(Inf) below as rand is called repeatedly internally # even for generating only one random value from t - SamplerSimple(t, Sampler(rng, linearindices(t.slots), Val(Inf))) + SamplerSimple(t, Sampler(rng, LinearIndices(t.slots), Val(Inf))) end function rand(rng::AbstractRNG, sp::SamplerSimple{<:Dict,<:Sampler}) diff --git a/stdlib/Test/src/Test.jl b/stdlib/Test/src/Test.jl index 64f8b3a6890d50..4a0dda53015c87 100644 --- a/stdlib/Test/src/Test.jl +++ b/stdlib/Test/src/Test.jl @@ -1543,7 +1543,7 @@ begin function test_approx_eq(va, vb, Eps, astr, bstr) va = approx_full(va) vb = approx_full(vb) - la, lb = length(linearindices(va)), length(linearindices(vb)) + la, lb = length(LinearIndices(va)), length(LinearIndices(vb)) if la != lb error("lengths of ", astr, " and ", bstr, " do not match: ", "\n ", astr, " (length $la) = ", va, @@ -1573,7 +1573,7 @@ begin array_eps(a) = eps(float(maximum(x->(isfinite(x) ? abs(x) : oftype(x,NaN)), a))) test_approx_eq(va, vb, astr, bstr) = - test_approx_eq(va, vb, 1E4*length(linearindices(va))*max(array_eps(va), array_eps(vb)), astr, bstr) + test_approx_eq(va, vb, 1E4*length(LinearIndices(va))*max(array_eps(va), array_eps(vb)), astr, bstr) """ @test_approx_eq_eps(a, b, tol) diff --git a/test/abstractarray.jl b/test/abstractarray.jl index c99fe4c9ba5e2e..dfc26bf10c74af 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -461,13 +461,13 @@ function test_primitives(::Type{T}, shape, ::Type{TestAbstractArray}) where T # last(a) @test last(B) == B[lastindex(B)] == B[end] == A[end] - @test lastindex(B) == lastindex(A) == last(linearindices(B)) + @test lastindex(B) == lastindex(A) == last(LinearIndices(B)) @test lastindex(B, 1) == lastindex(A, 1) == last(axes(B, 1)) @test lastindex(B, 2) == lastindex(A, 2) == last(axes(B, 2)) # first(a) @test first(B) == B[firstindex(B)] == B[1] == A[1] # TODO: use B[begin] once parser transforms it - @test firstindex(B) == firstindex(A) == first(linearindices(B)) + @test firstindex(B) == firstindex(A) == first(LinearIndices(B)) @test firstindex(B, 1) == firstindex(A, 1) == first(axes(B, 1)) @test firstindex(B, 2) == firstindex(A, 2) == first(axes(B, 2)) @@ -871,7 +871,7 @@ end @test CR[i,j] == CartesianIndex(i,j) end - for i_lin in linearindices(CR) + for i_lin in LinearIndices(CR) i = (i_lin-1) % length(xrng) + 1 j = (i_lin-i) ÷ length(xrng) + 1 @test CR[i_lin] == CartesianIndex(xrng[i],yrng[j])