From fc65731205c6307ef6a8162be37810a24306219c Mon Sep 17 00:00:00 2001 From: Bart Janssens Date: Wed, 22 Nov 2017 23:50:02 +0100 Subject: [PATCH 1/3] Make CartesianRange an AbstractArray --- base/multidimensional.jl | 6 +++++- test/abstractarray.jl | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/base/multidimensional.jl b/base/multidimensional.jl index e7978079d3da4..b4f2f28867134 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -185,7 +185,7 @@ module IteratorsMD CartesianIndex(2, 2, 2) ``` """ - struct CartesianRange{N,R<:NTuple{N,AbstractUnitRange{Int}}} + struct CartesianRange{N,R<:NTuple{N,AbstractUnitRange{Int}}} <: AbstractArray{CartesianIndex{N},N} indices::R end @@ -222,6 +222,10 @@ module IteratorsMD convert(::Type{Tuple{Vararg{UnitRange}}}, R::CartesianRange) = convert(Tuple{Vararg{UnitRange{Int}}}, R) + # AbstractArray implementation + Base.IndexStyle(::Type{CartesianRange{N,R}}) where {N,R} = IndexCartesian() + @inline Base.getindex(iter::CartesianRange{N,R}, I::Vararg{Int, N}) where {N,R} = CartesianIndex(first.(iter.indices) .- 1 .+ I) + ndims(R::CartesianRange) = ndims(typeof(R)) ndims(::Type{CartesianRange{N}}) where {N} = N ndims(::Type{CartesianRange{N,TT}}) where {N,TT} = N diff --git a/test/abstractarray.jl b/test/abstractarray.jl index ef8f72d391cb5..d054b1e35dc21 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -877,3 +877,21 @@ end Z = Array{Int,0}(uninitialized); Z[] = 17 @test Z == collect(Z) == copy(Z) end + +@testset "CartesianRange" begin + xrng = 2:4 + yrng = 1:5 + CR = CartesianRange((xrng,yrng)) + + for (i,i_idx) in enumerate(xrng) + for (j,j_idx) in enumerate(yrng) + @test CR[i,j] == CartesianIndex(i_idx,j_idx) + end + end + + 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]) + end +end From 60dcd6fc91e2495f95f334c20e145a660721d938 Mon Sep 17 00:00:00 2001 From: Bart Janssens Date: Tue, 28 Nov 2017 17:33:31 +0100 Subject: [PATCH 2/3] CartesianRange constructor and eachindex --- NEWS.md | 5 ++++ base/abstractarray.jl | 11 +++++++++ base/multidimensional.jl | 46 ++++++++++++++++++++++++++++++++++++ doc/src/devdocs/subarrays.md | 5 ++-- test/abstractarray.jl | 3 +++ 5 files changed, 67 insertions(+), 3 deletions(-) diff --git a/NEWS.md b/NEWS.md index b4968a8188f09..34bbde1663c3f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -401,6 +401,11 @@ Library improvements This supersedes the old behavior of reinterpret on Arrays. As a result, reinterpreting arrays with different alignment requirements (removed in 0.6) is once again allowed ([#23750]). + * `CartesianRange` changes ([#24715]): + - Inherits from `AbstractArray` + - Constructor taking an array + - `eachindex` returns the linear indices into a reshaped array, as `sub2ind` alternative + Compiler/Runtime improvements ----------------------------- diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 721dfe78d3baf..520267be0aeff 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -811,6 +811,17 @@ if all inputs have fast linear indexing, a [`CartesianRange`](@ref) otherwise). If the arrays have different sizes and/or dimensionalities, `eachindex` will return an iterable that spans the largest range along each dimension. + +For a CartesianRange, this returns a reshaped range of the linear indices into +the range, e.g.: + +```jldoctest +julia> eachindex(CartesianRange((1:2,1:3))) +2×3 reshape(::Base.OneTo{Int64}, 2, 3) with eltype Int64: + 1 3 5 + 2 4 6 +``` + """ eachindex(A::AbstractArray) = (@_inline_meta(); eachindex(IndexStyle(A), A)) diff --git a/base/multidimensional.jl b/base/multidimensional.jl index b4f2f28867134..4815a9ac866ea 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -172,6 +172,11 @@ module IteratorsMD Consequently these can be useful for writing algorithms that work in arbitrary dimensions. + CartesianRange(A::AbstractArray) -> R + + As a convenience, constructing a CartesianRange from an array makes a + range of its indices. + # Examples ```jldoctest julia> foreach(println, CartesianRange((2, 2, 2))) @@ -183,6 +188,44 @@ module IteratorsMD CartesianIndex(2, 1, 2) CartesianIndex(1, 2, 2) CartesianIndex(2, 2, 2) + + julia> CartesianRange(ones(2,3)) + 2×3 CartesianRange{2,Tuple{Base.OneTo{Int64},Base.OneTo{Int64}}}: + CartesianIndex(1, 1) CartesianIndex(1, 2) CartesianIndex(1, 3) + CartesianIndex(2, 1) CartesianIndex(2, 2) CartesianIndex(2, 3) + ``` + + ## Conversion between linear and cartesian indices + + Linear index to cartesian index conversion exploits the fact that a + `CartesianRange` is an `AbstractArray` and can be indexed linearly: + + ```jldoctest subarray + julia> cartesian = CartesianRange(1:3,1:2) + 3×2 CartesianRange{2,Tuple{UnitRange{Int64},UnitRange{Int64}}}: + CartesianIndex(1, 1) CartesianIndex(1, 2) + CartesianIndex(2, 1) CartesianIndex(2, 2) + CartesianIndex(3, 1) CartesianIndex(3, 2) + + julia> cartesian[4] + CartesianIndex(1, 2) + ``` + + For cartesian to linear index conversion, [`eachindex`](@ref) returns a + reshaped version of the linear indices when called on a `CartesianRange`: + + ```jldoctest subarray + julia> linear = eachindex(cartesian) + 3×2 reshape(::Base.OneTo{Int64}, 3, 2) with eltype Int64: + 1 4 + 2 5 + 3 6 + + julia> linear[1,2] + 4 + + julia> linear[cartesian[4]] + 4 ``` """ struct CartesianRange{N,R<:NTuple{N,AbstractUnitRange{Int}}} <: AbstractArray{CartesianIndex{N},N} @@ -204,6 +247,8 @@ module IteratorsMD CartesianRange(inds::NTuple{N,Union{<:Integer,AbstractUnitRange{<:Integer}}}) where {N} = CartesianRange(map(i->first(i):last(i), inds)) + CartesianRange(A::AbstractArray) = CartesianRange(indices(A)) + convert(::Type{Tuple{}}, R::CartesianRange{0}) = () convert(::Type{NTuple{N,AbstractUnitRange{Int}}}, R::CartesianRange{N}) where {N} = R.indices @@ -225,6 +270,7 @@ module IteratorsMD # AbstractArray implementation Base.IndexStyle(::Type{CartesianRange{N,R}}) where {N,R} = IndexCartesian() @inline Base.getindex(iter::CartesianRange{N,R}, I::Vararg{Int, N}) where {N,R} = CartesianIndex(first.(iter.indices) .- 1 .+ I) + Base.eachindex(iter::CartesianRange) = reshape(linearindices(iter), size(iter)) ndims(R::CartesianRange) = ndims(typeof(R)) ndims(::Type{CartesianRange{N}}) where {N} = N diff --git a/doc/src/devdocs/subarrays.md b/doc/src/devdocs/subarrays.md index 71d6f332880c9..57db3a6e592b4 100644 --- a/doc/src/devdocs/subarrays.md +++ b/doc/src/devdocs/subarrays.md @@ -22,9 +22,8 @@ computation (such as interpolation), and the type under discussion here, `SubArr For these types, the underlying information is more naturally described in terms of cartesian indexes. -You can manually convert from a cartesian index to a linear index with `sub2ind`, and vice versa -using `ind2sub`. `getindex` and `setindex!` functions for `AbstractArray` types may include similar -operations. +The `getindex` and `setindex!` functions for `AbstractArray` types may include automatic conversion +between indexing types. For explicit conversion, [`CartesianRange`](@ref) can be used. While converting from a cartesian index to a linear index is fast (it's just multiplication and addition), converting from a linear index to a cartesian index is very slow: it relies on the diff --git a/test/abstractarray.jl b/test/abstractarray.jl index d054b1e35dc21..7b4c45a57fd8f 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -894,4 +894,7 @@ end j = (i_lin-i) ÷ length(xrng) + 1 @test CR[i_lin] == CartesianIndex(xrng[i],yrng[j]) end + + @test CartesianRange(ones(2,3)) == CartesianRange((2,3)) + @test eachindex(CartesianRange((2,3))) == [1 3 5; 2 4 6] end From d26672d0e9bd7793450eef7e50855daea80ed6ba Mon Sep 17 00:00:00 2001 From: Bart Janssens Date: Fri, 1 Dec 2017 12:50:26 +0100 Subject: [PATCH 3/3] Deprecate sub2ind and ind2sub --- NEWS.md | 15 ++- base/abstractarray.jl | 154 ++++++++---------------------- base/array.jl | 2 +- base/deprecated.jl | 21 ++++ base/exports.jl | 3 +- base/multidimensional.jl | 67 +++++++++---- base/precompile.jl | 6 +- base/reshapedarray.jl | 4 +- base/sparse/sparsematrix.jl | 10 +- base/sparse/sparsevector.jl | 8 +- base/subarray.jl | 2 +- doc/src/devdocs/offset-arrays.md | 2 +- doc/src/manual/metaprogramming.md | 2 +- doc/src/stdlib/arrays.md | 3 +- test/abstractarray.jl | 75 ++++++++------- test/arrayops.jl | 2 +- test/euler.jl | 7 +- 17 files changed, 184 insertions(+), 199 deletions(-) diff --git a/NEWS.md b/NEWS.md index 34bbde1663c3f..1efe6cea171e9 100644 --- a/NEWS.md +++ b/NEWS.md @@ -202,6 +202,13 @@ This section lists changes that do not have deprecation warnings. longer present. Use `first(R)` and `last(R)` to obtain start/stop. ([#20974]) + * `CartesianRange` inherits from AbstractArray and construction with an + `AbstractArray` argument constructs the indices for that array. Consequently, + linear indexing can be used to provide linear-to-cartesian conversion ([#24715]) + + * The type `CartesianToLinear` has been added, providing conversion from + cartesian incices to linear indices using the normal indexing operation. ([#24715]) + * The `Diagonal`, `Bidiagonal`, `Tridiagonal` and `SymTridiagonal` type definitions have changed from `Diagonal{T}`, `Bidiagonal{T}`, `Tridiagonal{T}` and `SymTridiagonal{T}` to `Diagonal{T,V<:AbstractVector{T}}`, `Bidiagonal{T,V<:AbstractVector{T}}`, @@ -401,11 +408,6 @@ Library improvements This supersedes the old behavior of reinterpret on Arrays. As a result, reinterpreting arrays with different alignment requirements (removed in 0.6) is once again allowed ([#23750]). - * `CartesianRange` changes ([#24715]): - - Inherits from `AbstractArray` - - Constructor taking an array - - `eachindex` returns the linear indices into a reshaped array, as `sub2ind` alternative - Compiler/Runtime improvements ----------------------------- @@ -704,6 +706,8 @@ Deprecated or removed * `cumsum`, `cumprod`, `accumulate`, and their mutating versions now require a `dim` argument instead of defaulting to using the first dimension ([#24684]). + * `sub2ind` and `ind2sub` are deprecated in favor of using `CartesianRange` and `CartesianToLinear` ([#24715]). + Command-line option changes --------------------------- @@ -1668,3 +1672,4 @@ Command-line option changes [#24320]: https://github.com/JuliaLang/julia/issues/24320 [#24396]: https://github.com/JuliaLang/julia/issues/24396 [#24413]: https://github.com/JuliaLang/julia/issues/24413 +[#24715]: https://github.com/JuliaLang/julia/issues/24715 diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 520267be0aeff..205fb4d05f9c0 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -308,9 +308,8 @@ type: The default is `IndexCartesian()`. Julia's internal indexing machinery will automatically (and invisibly) -convert all indexing operations into the preferred style using -[`sub2ind`](@ref) or [`ind2sub`](@ref). This allows users to access -elements of your array using any indexing style, even when explicit +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 @@ -811,17 +810,6 @@ if all inputs have fast linear indexing, a [`CartesianRange`](@ref) otherwise). If the arrays have different sizes and/or dimensionalities, `eachindex` will return an iterable that spans the largest range along each dimension. - -For a CartesianRange, this returns a reshaped range of the linear indices into -the range, e.g.: - -```jldoctest -julia> eachindex(CartesianRange((1:2,1:3))) -2×3 reshape(::Base.OneTo{Int64}, 2, 3) with eltype Int64: - 1 3 5 - 2 4 6 -``` - """ eachindex(A::AbstractArray) = (@_inline_meta(); eachindex(IndexStyle(A), A)) @@ -955,7 +943,7 @@ end _to_linear_index(A::AbstractArray, i::Int) = i _to_linear_index(A::AbstractVector, i::Int, I::Int...) = i _to_linear_index(A::AbstractArray) = 1 -_to_linear_index(A::AbstractArray, I::Int...) = (@_inline_meta; sub2ind(A, I...)) +_to_linear_index(A::AbstractArray, I::Int...) = (@_inline_meta; _sub2ind(A, I...)) ## IndexCartesian Scalar indexing: Canonical method is full dimensionality of Ints function _getindex(::IndexCartesian, A::AbstractArray, I::Vararg{Int,M}) where M @@ -989,8 +977,8 @@ _to_subscript_indices(A, J::Tuple, Jrem::Tuple) = J # already bounds-checked, sa _to_subscript_indices(A::AbstractArray{T,N}, I::Vararg{Int,N}) where {T,N} = I _remaining_size(::Tuple{Any}, t::Tuple) = t _remaining_size(h::Tuple, t::Tuple) = (@_inline_meta; _remaining_size(tail(h), tail(t))) -_unsafe_ind2sub(::Tuple{}, i) = () # ind2sub may throw(BoundsError()) in this case -_unsafe_ind2sub(sz, i) = (@_inline_meta; ind2sub(sz, i)) +_unsafe_ind2sub(::Tuple{}, i) = () # _ind2sub may throw(BoundsError()) in this case +_unsafe_ind2sub(sz, i) = (@_inline_meta; _ind2sub(sz, i)) ## Setindex! is defined similarly. We first dispatch to an internal _setindex! # function that allows dispatch on array storage @@ -1573,73 +1561,43 @@ function (==)(A::AbstractArray, B::AbstractArray) return true end -# sub2ind and ind2sub +# _sub2ind and _ind2sub # fallbacks -function sub2ind(A::AbstractArray, I...) +function _sub2ind(A::AbstractArray, I...) @_inline_meta - sub2ind(indices(A), I...) + _sub2ind(indices(A), I...) end -""" - ind2sub(a, index) -> subscripts - -Return a tuple of subscripts into array `a` corresponding to the linear index `index`. - -# Examples -```jldoctest -julia> A = ones(5,6,7); - -julia> ind2sub(A,35) -(5, 1, 2) - -julia> ind2sub(A,70) -(5, 2, 3) -``` -""" -function ind2sub(A::AbstractArray, ind) +function _ind2sub(A::AbstractArray, ind) @_inline_meta - ind2sub(indices(A), ind) + _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, i, j, k...) -> index - -The inverse of [`ind2sub`](@ref), return the linear index corresponding to the provided subscripts. +_sub2ind(::Tuple{}) = 1 +_sub2ind(::DimsInteger) = 1 +_sub2ind(::Indices) = 1 +_sub2ind(::Tuple{}, I::Integer...) = (@_inline_meta; _sub2ind_recurse((), 1, 1, I...)) -# Examples -```jldoctest -julia> sub2ind((5,6,7),1,2,3) -66 - -julia> sub2ind((5,6,7),1,6,3) -86 -``` -""" -sub2ind(dims::DimsInteger, I::Integer...) = (@_inline_meta; _sub2ind(dims, 1, 1, I...)) -sub2ind(inds::Indices, I::Integer...) = (@_inline_meta; _sub2ind(inds, 1, 1, I...)) +# Generic cases +_sub2ind(dims::DimsInteger, I::Integer...) = (@_inline_meta; _sub2ind_recurse(dims, 1, 1, I...)) +_sub2ind(inds::Indices, I::Integer...) = (@_inline_meta; _sub2ind_recurse(inds, 1, 1, I...)) # 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...) = +_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(inds::Tuple{OneTo}, I::Integer...) = (@_inline_meta; _sub2ind_recurse(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...) +_sub2ind_recurse(::Any, L, ind) = ind +function _sub2ind_recurse(::Tuple{}, L, ind, i::Integer, I::Integer...) @_inline_meta - _sub2ind((), L, ind+(i-1)*L, I...) + _sub2ind_recurse((), L, ind+(i-1)*L, I...) end -function _sub2ind(inds, L, ind, i::Integer, I::Integer...) +function _sub2ind_recurse(inds, L, ind, i::Integer, I::Integer...) @_inline_meta r1 = inds[1] - _sub2ind(tail(inds), nextL(L, r1), ind+offsetin(i, r1)*L, I...) + _sub2ind_recurse(tail(inds), nextL(L, r1), ind+offsetin(i, r1)*L, I...) end nextL(L, l::Integer) = L*l @@ -1647,42 +1605,23 @@ nextL(L, r::AbstractUnitRange) = L*unsafe_length(r) offsetin(i, l::Integer) = i-1 offsetin(i, r::AbstractUnitRange) = i-first(r) -ind2sub(::Tuple{}, ind::Integer) = (@_inline_meta; ind == 1 ? () : throw(BoundsError())) - -""" - ind2sub(dims, index) -> subscripts - -Return a tuple of subscripts into an array with dimensions `dims`, -corresponding to the linear index `index`. - -# Examples -```jldoctest -julia> ind2sub((3,4),2) -(2, 1) - -julia> ind2sub((3,4),3) -(3, 1) - -julia> ind2sub((3,4),4) -(1, 2) -``` -""" -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) = +_ind2sub(::Tuple{}, ind::Integer) = (@_inline_meta; ind == 1 ? () : throw(BoundsError())) +_ind2sub(dims::DimsInteger, ind::Integer) = (@_inline_meta; _ind2sub_recurse(dims, ind-1)) +_ind2sub(inds::Indices, ind::Integer) = (@_inline_meta; _ind2sub_recurse(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(inds::Tuple{OneTo}, ind::Integer) = (ind,) -_ind2sub(::Tuple{}, ind) = (ind+1,) -function _ind2sub(indslast::NTuple{1}, ind) +_ind2sub_recurse(::Tuple{}, ind) = (ind+1,) +function _ind2sub_recurse(indslast::NTuple{1}, ind) @_inline_meta (_lookup(ind, indslast[1]),) end -function _ind2sub(inds, ind) +function _ind2sub_recurse(inds, ind) @_inline_meta r1 = inds[1] indnext, f, l = _div(ind, r1) - (ind-l*indnext+f, _ind2sub(tail(inds), indnext)...) + (ind-l*indnext+f, _ind2sub_recurse(tail(inds), indnext)...) end _lookup(ind, d::Integer) = ind+1 @@ -1691,12 +1630,12 @@ _div(ind, d::Integer) = div(ind, d), 1, d _div(ind, r::AbstractUnitRange) = (d = unsafe_length(r); (div(ind, d), first(r), d)) # Vectorized forms -function sub2ind(inds::Indices{1}, I1::AbstractVector{T}, I::AbstractVector{T}...) where T<:Integer +function _sub2ind(inds::Indices{1}, I1::AbstractVector{T}, I::AbstractVector{T}...) where T<:Integer throw(ArgumentError("Linear indexing is not defined for one-dimensional arrays")) end -sub2ind(inds::Tuple{OneTo}, I1::AbstractVector{T}, I::AbstractVector{T}...) where {T<:Integer} = +_sub2ind(inds::Tuple{OneTo}, I1::AbstractVector{T}, I::AbstractVector{T}...) where {T<:Integer} = _sub2ind_vecs(inds, I1, I...) -sub2ind(inds::Union{DimsInteger,Indices}, I1::AbstractVector{T}, I::AbstractVector{T}...) where {T<:Integer} = +_sub2ind(inds::Union{DimsInteger,Indices}, I1::AbstractVector{T}, I::AbstractVector{T}...) where {T<:Integer} = _sub2ind_vecs(inds, I1, I...) function _sub2ind_vecs(inds, I::AbstractVector...) I1 = I[1] @@ -1712,21 +1651,21 @@ end function _sub2ind!(Iout, inds, Iinds, I) @_noinline_meta for i in Iinds - # Iout[i] = sub2ind(inds, map(Ij -> Ij[i], I)...) + # Iout[i] = _sub2ind(inds, map(Ij -> Ij[i], I)...) Iout[i] = sub2ind_vec(inds, i, I) end Iout end -sub2ind_vec(inds, i, I) = (@_inline_meta; sub2ind(inds, _sub2ind_vec(i, I...)...)) +sub2ind_vec(inds, i, I) = (@_inline_meta; _sub2ind(inds, _sub2ind_vec(i, I...)...)) _sub2ind_vec(i, I1, I...) = (@_inline_meta; (I1[i], _sub2ind_vec(i, I...)...)) _sub2ind_vec(i) = () -function ind2sub(inds::Union{DimsInteger{N},Indices{N}}, ind::AbstractVector{<:Integer}) where N +function _ind2sub(inds::Union{DimsInteger{N},Indices{N}}, ind::AbstractVector{<:Integer}) where N M = length(ind) t = ntuple(n->similar(ind),Val(N)) for (i,idx) in pairs(IndexLinear(), ind) - sub = ind2sub(inds, idx) + sub = _ind2sub(inds, idx) for j = 1:N t[j][i] = sub[j] end @@ -1734,17 +1673,6 @@ function ind2sub(inds::Union{DimsInteger{N},Indices{N}}, ind::AbstractVector{<:I t end -function ind2sub!(sub::Array{T}, dims::Tuple{Vararg{T}}, ind::T) where T<:Integer - ndims = length(dims) - for i=1:ndims-1 - ind2 = div(ind-1,dims[i])+1 - sub[i] = ind - dims[i]*(ind2-1) - ind = ind2 - end - sub[ndims] = ind - return sub -end - ## iteration utilities ## """ diff --git a/base/array.jl b/base/array.jl index 083e4a4323f7f..868b678f2e188 100644 --- a/base/array.jl +++ b/base/array.jl @@ -135,7 +135,7 @@ sizeof(a::Array) = Core.sizeof(a) function isassigned(a::Array, i::Int...) @_inline_meta - ii = (sub2ind(size(a), i...) % UInt) - 1 + ii = (_sub2ind(size(a), i...) % UInt) - 1 @boundscheck ii < length(a) % UInt || return false ccall(:jl_array_isassigned, Cint, (Any, UInt), a, ii) == 1 end diff --git a/base/deprecated.jl b/base/deprecated.jl index d789c5cd47dd9..1085d31e557a8 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -2172,6 +2172,27 @@ end @deprecate merge!(repo::LibGit2.GitRepo, args...; kwargs...) LibGit2.merge!(repo, args...; kwargs...) +# sub2ind and ind2sub deprecation (PR #24715) +@deprecate ind2sub(A::AbstractArray, ind) CartesianRange(A)[ind] +@deprecate ind2sub(::Tuple{}, ind::Integer) CartesianRange()[ind] +@deprecate ind2sub(dims::Tuple{Vararg{Integer,N}} where N, ind::Integer) CartesianRange(dims)[ind] +@deprecate ind2sub(inds::Tuple{Base.OneTo}, ind::Integer) CartesianRange(inds)[ind] +@deprecate ind2sub(inds::Tuple{AbstractUnitRange}, ind::Integer) CartesianRange(inds)[ind] +@deprecate ind2sub(inds::Tuple{Vararg{AbstractUnitRange,N}} where N, ind::Integer) CartesianRange(inds)[ind] +@deprecate ind2sub(inds::Union{DimsInteger{N},Indices{N}} where N, ind::AbstractVector{<:Integer}) CartesianRange(inds)[ind] + +@deprecate sub2ind(A::AbstractArray, I...) CartesianToLinear(A)[I...] +@deprecate sub2ind(dims::Tuple{}) CartesianToLinear(dims)[] +@deprecate sub2ind(dims::DimsInteger) CartesianToLinear(dims)[] +@deprecate sub2ind(dims::Indices) CartesianToLinear(dims)[] +@deprecate sub2ind(dims::Tuple{}, I::Integer...) CartesianToLinear(dims)[I...] +@deprecate sub2ind(dims::DimsInteger, I::Integer...) CartesianToLinear(dims)[I...] +@deprecate sub2ind(inds::Indices, I::Integer...) CartesianToLinear(inds)[I...] +@deprecate sub2ind(inds::Tuple{OneTo}, I::Integer...) CartesianToLinear(inds)[I...] +@deprecate sub2ind(inds::Tuple{OneTo}, i::Integer) CartesianToLinear(inds)[i] +@deprecate sub2ind(inds::Tuple{OneTo}, I1::AbstractVector{T}, I::AbstractVector{T}...) where {T<:Integer} CartesianToLinear(inds)[CartesianIndex.(I1, I...)] +@deprecate sub2ind(inds::Union{DimsInteger,Indices}, I1::AbstractVector{T}, I::AbstractVector{T}...) where {T<:Integer} CartesianToLinear(inds)[CartesianIndex.(I1, I...)] + # END 0.7 deprecations # BEGIN 1.0 deprecations diff --git a/base/exports.jl b/base/exports.jl index 8213ecce7b584..de8c274bb3158 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -40,6 +40,7 @@ export BufferStream, CartesianIndex, CartesianRange, + CartesianToLinear, Channel, Cmd, Colon, @@ -453,7 +454,6 @@ export flipdim, hcat, hvcat, - ind2sub, indexin, indices, indmax, @@ -523,7 +523,6 @@ export step, stride, strides, - sub2ind, sum!, sum, sum_kbn, diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 4815a9ac866ea..7a597e9533048 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, CartesianRange + export CartesianIndex, CartesianRange, CartesianToLinear """ CartesianIndex(i, j, k...) -> I @@ -211,22 +211,7 @@ module IteratorsMD CartesianIndex(1, 2) ``` - For cartesian to linear index conversion, [`eachindex`](@ref) returns a - reshaped version of the linear indices when called on a `CartesianRange`: - - ```jldoctest subarray - julia> linear = eachindex(cartesian) - 3×2 reshape(::Base.OneTo{Int64}, 3, 2) with eltype Int64: - 1 4 - 2 5 - 3 6 - - julia> linear[1,2] - 4 - - julia> linear[cartesian[4]] - 4 - ``` + For cartesian to linear index conversion, see [`CartesianToLinear`](@ref). """ struct CartesianRange{N,R<:NTuple{N,AbstractUnitRange{Int}}} <: AbstractArray{CartesianIndex{N},N} indices::R @@ -270,7 +255,6 @@ module IteratorsMD # AbstractArray implementation Base.IndexStyle(::Type{CartesianRange{N,R}}) where {N,R} = IndexCartesian() @inline Base.getindex(iter::CartesianRange{N,R}, I::Vararg{Int, N}) where {N,R} = CartesianIndex(first.(iter.indices) .- 1 .+ I) - Base.eachindex(iter::CartesianRange) = reshape(linearindices(iter), size(iter)) ndims(R::CartesianRange) = ndims(typeof(R)) ndims(::Type{CartesianRange{N}}) where {N} = N @@ -397,6 +381,53 @@ module IteratorsMD start(iter::Reverse{<:CartesianRange{0}}) = false next(iter::Reverse{<:CartesianRange{0}}, state) = CartesianIndex(), true done(iter::Reverse{<:CartesianRange{0}}, state) = state + + """ + CartesianToLinear(inds::CartesianRange) -> R + CartesianToLinear(sz::Dims) -> R + CartesianToLinear(istart:istop, jstart:jstop, ...) -> R + + Define a mapping between cartesian indices and the corresponding linear index into a CartesianRange + + # Example + + The main purpose of this type is intuitive conversion from cartesian to linear indexing: + + ```jldoctest subarray + julia> linear = CartesianToLinear(1:3,1:2) + CartesianToLinear{2,Tuple{UnitRange{Int64},UnitRange{Int64}}} with indices 1:3×1:2: + 1 4 + 2 5 + 3 6 + + julia> linear[1,2] + 4 + ``` + """ + struct CartesianToLinear{N,R<:NTuple{N,AbstractUnitRange{Int}}} <: AbstractArray{Int,N} + indices::R + end + + CartesianToLinear(inds::CartesianRange{N,R}) where {N,R} = CartesianToLinear{N,R}(inds.indices) + CartesianToLinear(::Tuple{}) = CartesianToLinear(CartesianRange(())) + CartesianToLinear(inds::NTuple{N,AbstractUnitRange{Int}}) where {N} = CartesianToLinear(CartesianRange(inds)) + CartesianToLinear(inds::Vararg{AbstractUnitRange{Int},N}) where {N} = CartesianToLinear(CartesianRange(inds)) + CartesianToLinear(inds::NTuple{N,AbstractUnitRange{<:Integer}}) where {N} = CartesianToLinear(CartesianRange(inds)) + CartesianToLinear(inds::Vararg{AbstractUnitRange{<:Integer},N}) where {N} = CartesianToLinear(CartesianRange(inds)) + CartesianToLinear(index::CartesianIndex) = CartesianToLinear(CartesianRange(index)) + CartesianToLinear(sz::NTuple{N,<:Integer}) where {N} = CartesianToLinear(CartesianRange(sz)) + CartesianToLinear(inds::NTuple{N,Union{<:Integer,AbstractUnitRange{<:Integer}}}) where {N} = CartesianToLinear(CartesianRange(inds)) + CartesianToLinear(A::AbstractArray) = CartesianToLinear(CartesianRange(A)) + + # AbstractArray implementation + Base.IndexStyle(::Type{CartesianToLinear{N,R}}) where {N,R} = IndexCartesian() + Base.indices(iter::CartesianToLinear{N,R}) where {N,R} = iter.indices + @inline function Base.getindex(iter::CartesianToLinear{N,R}, I::Vararg{Int, N}) where {N,R} + dims = length.(iter.indices) + #without the inbounds, this is slower than Base._sub2ind(iter.indices, I...) + @inbounds result = reshape(1:prod(dims), dims)[(I .- first.(iter.indices) .+ 1)...] + return result + end end # IteratorsMD diff --git a/base/precompile.jl b/base/precompile.jl index accf477cad0ac..68fbcf831c1f2 100644 --- a/base/precompile.jl +++ b/base/precompile.jl @@ -1625,9 +1625,9 @@ precompile(Tuple{typeof(Base.print_matrix_vdots), Base.IOContext{Base.Terminals. precompile(Tuple{typeof(Base.print_matrix), Base.IOContext{Base.Terminals.TTYTerminal}, Array{Int64, 1}, String, String, String, String, String, String, Int64, Int64}) precompile(Tuple{typeof(Base.alignment), Base.IOContext{Base.Terminals.TTYTerminal}, Array{Int64, 1}, Array{Int64, 1}, Array{Int64, 1}, Int64, Int64, Int64}) precompile(Tuple{getfield(Base, Symbol("#kw##sprint")), Array{Any, 1}, typeof(Base.sprint), Int64, typeof(Base.show), Int64}) -precompile(Tuple{typeof(Base.sub2ind), Tuple{Int64}, Int64, Int64}) -precompile(Tuple{typeof(Base._sub2ind), Tuple{Int64}, Int64, Int64, Int64, Int64}) -precompile(Tuple{typeof(Base._sub2ind), Tuple{}, Int64, Int64, Int64}) +precompile(Tuple{typeof(Base._sub2ind), Tuple{Int64}, Int64, Int64}) +precompile(Tuple{typeof(Base._sub2ind_recurse), Tuple{Int64}, Int64, Int64, Int64, Int64}) +precompile(Tuple{typeof(Base._sub2ind_recurse), Tuple{}, Int64, Int64, Int64}) precompile(Tuple{typeof(Base.first), Array{Int64, 1}}) precompile(Tuple{typeof(Base.print_matrix_row), Base.IOContext{Base.Terminals.TTYTerminal}, Array{Int64, 1}, Array{Tuple{Int64, Int64}, 1}, Int64, Array{Int64, 1}, String}) precompile(Tuple{typeof(Base.print), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.OneTo{Int64}}) diff --git a/base/reshapedarray.jl b/base/reshapedarray.jl index fef634061165b..304b84786f604 100644 --- a/base/reshapedarray.jl +++ b/base/reshapedarray.jl @@ -204,7 +204,7 @@ end end @inline function _unsafe_getindex(A::ReshapedArray{T,N}, indexes::Vararg{Int,N}) where {T,N} - i = sub2ind(size(A), indexes...) + i = Base._sub2ind(size(A), indexes...) I = ind2sub_rs(A.mi, i) _unsafe_getindex_rs(parent(A), I) end @@ -227,7 +227,7 @@ end end @inline function _unsafe_setindex!(A::ReshapedArray{T,N}, val, indexes::Vararg{Int,N}) where {T,N} - @inbounds parent(A)[ind2sub_rs(A.mi, sub2ind(size(A), indexes...))...] = val + @inbounds parent(A)[ind2sub_rs(A.mi, Base._sub2ind(size(A), indexes...))...] = val val end diff --git a/base/sparse/sparsematrix.jl b/base/sparse/sparsematrix.jl index 0567b91758877..7b06ed2b2c485 100644 --- a/base/sparse/sparsematrix.jl +++ b/base/sparse/sparsematrix.jl @@ -1279,7 +1279,7 @@ function find(p::Function, S::SparseMatrixCSC) end sz = size(S) I, J = _findn(p, S) - return sub2ind(sz, I, J) + return Base._sub2ind(sz, I, J) end findn(S::SparseMatrixCSC{Tv,Ti}) where {Tv,Ti} = _findn(x->true, S) @@ -2273,10 +2273,10 @@ function getindex(A::SparseMatrixCSC{Tv,Ti}, I::AbstractArray) where {Tv,Ti} for i in 1:n ((I[i] < 1) | (I[i] > nA)) && throw(BoundsError()) - row,col = ind2sub(szA, I[i]) + row,col = Base._ind2sub(szA, I[i]) for r in colptrA[col]:(colptrA[col+1]-1) @inbounds if rowvalA[r] == row - rowB,colB = ind2sub(szB, i) + rowB,colB = Base._ind2sub(szB, i) colptrB[colB+1] += 1 rowvalB[idxB] = rowB nzvalB[idxB] = nzvalA[r] @@ -2778,7 +2778,7 @@ function setindex!(A::SparseMatrixCSC, x, I::AbstractVector{<:Real}) sxidx = S[xidx] (sxidx < n) && (I[sxidx] == I[sxidx+1]) && continue - row,col = ind2sub(szA, I[sxidx]) + row,col = Base._ind2sub(szA, I[sxidx]) v = isa(x, AbstractArray) ? x[sxidx] : x if col > lastcol @@ -3471,7 +3471,7 @@ function hash(A::SparseMatrixCSC{T}, h::UInt) where T for j = colptr[col]:colptr[col+1]-1 nz = nzval[j] isequal(nz, zero(T)) && continue - idx = sub2ind(sz, rowval[j], col) + idx = Base._sub2ind(sz, rowval[j], col) if idx != lastidx+1 || !isequal(nz, lastnz) # Run is over h = hashrun(lastnz, runlength, h) # Hash previous run h = hashrun(0, idx-lastidx-1, h) # Hash intervening zeros diff --git a/base/sparse/sparsevector.jl b/base/sparse/sparsevector.jl index 76e09d34ce39a..5ea791410bdb4 100644 --- a/base/sparse/sparsevector.jl +++ b/base/sparse/sparsevector.jl @@ -617,8 +617,8 @@ function getindex(A::SparseMatrixCSC{Tv}, I::AbstractUnitRange) where Tv rowvalB = Vector{Int}(uninitialized, nnzB) nzvalB = Vector{Tv}(uninitialized, nnzB) - rowstart,colstart = ind2sub(szA, first(I)) - rowend,colend = ind2sub(szA, last(I)) + rowstart,colstart = Base._ind2sub(szA, first(I)) + rowend,colend = Base._ind2sub(szA, last(I)) idxB = 1 @inbounds for col in colstart:colend @@ -627,7 +627,7 @@ function getindex(A::SparseMatrixCSC{Tv}, I::AbstractUnitRange) where Tv for r in colptrA[col]:(colptrA[col+1]-1) rowA = rowvalA[r] if minrow <= rowA <= maxrow - rowvalB[idxB] = sub2ind(szA, rowA, col) - first(I) + 1 + rowvalB[idxB] = Base._sub2ind(szA, rowA, col) - first(I) + 1 nzvalB[idxB] = nzvalA[r] idxB += 1 end @@ -655,7 +655,7 @@ function getindex(A::SparseMatrixCSC{Tv,Ti}, I::AbstractVector) where {Tv,Ti} idxB = 1 for i in 1:n ((I[i] < 1) | (I[i] > nA)) && throw(BoundsError(A, I)) - row,col = ind2sub(szA, I[i]) + row,col = Base._ind2sub(szA, I[i]) for r in colptrA[col]:(colptrA[col+1]-1) @inbounds if rowvalA[r] == row if idxB <= nnzB diff --git a/base/subarray.jl b/base/subarray.jl index 2e76551901376..7546644ce8db4 100644 --- a/base/subarray.jl +++ b/base/subarray.jl @@ -329,7 +329,7 @@ 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, i) _pointer(V::SubArray{<:Any,1}, i::Int) = pointer(V, (i,)) -_pointer(V::SubArray, i::Int) = pointer(V, ind2sub(indices(V), i)) +_pointer(V::SubArray, i::Int) = pointer(V, Base._ind2sub(indices(V), i)) function pointer(V::SubArray{T,N,<:Array,<:Tuple{Vararg{RangeIndex}}}, is::Tuple{Vararg{Int}}) where {T,N} index = first_index(V) diff --git a/doc/src/devdocs/offset-arrays.md b/doc/src/devdocs/offset-arrays.md index 7ad1dfb7bc6e6..8c1302f49b26e 100644 --- a/doc/src/devdocs/offset-arrays.md +++ b/doc/src/devdocs/offset-arrays.md @@ -81,7 +81,7 @@ Some algorithms are most conveniently (or efficiently) written in terms of a sin 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 `indices(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 sub2ind(shape, i...) and ind2sub(shape, ind) 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. +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 `indices` and `linearindices`, here is one way you could rewrite `mycopy!`: diff --git a/doc/src/manual/metaprogramming.md b/doc/src/manual/metaprogramming.md index b14ace6264800..e11f111d95e54 100644 --- a/doc/src/manual/metaprogramming.md +++ b/doc/src/manual/metaprogramming.md @@ -1264,7 +1264,7 @@ to build some more advanced (and valid) functionality... ### An advanced example -Julia's base library has a [`sub2ind`](@ref) function to calculate a linear index into an n-dimensional +Julia's base library has a an internal `sub2ind` function to calculate a linear index into an n-dimensional array, based on a set of n multilinear indices - in other words, to calculate the index `i` that can be used to index into an array `A` using `A[i]`, instead of `A[x,y,z,...]`. One possible implementation is the following: diff --git a/doc/src/stdlib/arrays.md b/doc/src/stdlib/arrays.md index 9ec8c580cc1fa..9c5eb9d19cede 100644 --- a/doc/src/stdlib/arrays.md +++ b/doc/src/stdlib/arrays.md @@ -48,8 +48,6 @@ Base.IndexStyle Base.conj! Base.stride Base.strides -Base.ind2sub -Base.sub2ind Base.LinAlg.checksquare ``` @@ -89,6 +87,7 @@ Base.isassigned Base.Colon Base.CartesianIndex Base.CartesianRange +Base.CartesianToLinear Base.to_indices Base.checkbounds Base.checkindex diff --git a/test/abstractarray.jl b/test/abstractarray.jl index 7b4c45a57fd8f..0ca1199ba17fd 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -112,62 +112,64 @@ end @test checkbounds(Bool, A, [CartesianIndex((5, 4))], 4) == false end -@testset "sub2ind & ind2sub" begin +@testset "index conversion" begin @testset "0-dimensional" begin - for i = 1:4 - @test sub2ind((), i) == i - end - @test sub2ind((), 2, 2) == 3 - @test ind2sub((), 1) == () - @test_throws BoundsError ind2sub((), 2) + @test CartesianToLinear()[1] == 1 + @test_throws BoundsError CartesianToLinear()[2] + @test CartesianToLinear()[1,1] == 1 + @test CartesianRange()[1] == CartesianIndex() + @test_throws BoundsError CartesianRange()[2] end @testset "1-dimensional" begin - for i = 1:4 - @test sub2ind((3,), i) == i - @test ind2sub((3,), i) == (i,) + for i = 1:3 + @test CartesianToLinear((3,))[i] == i + @test CartesianRange((3,))[i] == CartesianIndex(i,) end - @test sub2ind((3,), 2, 2) == 5 - @test_throws MethodError ind2sub((3,), 2, 2) + @test CartesianToLinear((3,))[2,1] == 2 + @test_throws BoundsError CartesianRange((3,))[2,2] # ambiguity btw cartesian indexing and linear indexing in 1d when # indices may be nontraditional - @test_throws ArgumentError sub2ind((1:3,), 2) - @test_throws ArgumentError ind2sub((1:3,), 2) + @test_throws ArgumentError Base._sub2ind((1:3,), 2) + @test_throws ArgumentError Base._ind2sub((1:3,), 2) end @testset "2-dimensional" begin k = 0 + cartesian = CartesianRange((4,3)) + linear = CartesianToLinear(cartesian) for j = 1:3, i = 1:4 - @test sub2ind((4,3), i, j) == (k+=1) - @test ind2sub((4,3), k) == (i,j) - @test sub2ind((1:4,1:3), i, j) == k - @test ind2sub((1:4,1:3), k) == (i,j) - @test sub2ind((0:3,3:5), i-1, j+2) == k - @test ind2sub((0:3,3:5), k) == (i-1, j+2) + @test linear[i,j] == (k+=1) + @test cartesian[k] == CartesianIndex(i,j) + @test CartesianToLinear(0:3,3:5)[i-1,j+2] == k + @test CartesianRange(0:3,3:5)[k] == CartesianIndex(i-1,j+2) end end @testset "3-dimensional" begin l = 0 for k = 1:2, j = 1:3, i = 1:4 - @test sub2ind((4,3,2), i, j, k) == (l+=1) - @test ind2sub((4,3,2), l) == (i,j,k) - @test sub2ind((1:4,1:3,1:2), i, j, k) == l - @test ind2sub((1:4,1:3,1:2), l) == (i,j,k) - @test sub2ind((0:3,3:5,-101:-100), i-1, j+2, k-102) == l - @test ind2sub((0:3,3:5,-101:-100), l) == (i-1, j+2, k-102) + @test CartesianToLinear((4,3,2))[i,j,k] == (l+=1) + @test CartesianRange((4,3,2))[l] == CartesianIndex(i,j,k) + @test CartesianToLinear(1:4,1:3,1:2)[i,j,k] == l + @test CartesianRange(1:4,1:3,1:2)[l] == CartesianIndex(i,j,k) + @test CartesianToLinear(0:3,3:5,-101:-100)[i-1,j+2,k-102] == l + @test CartesianRange(0:3,3:5,-101:-100)[l] == CartesianIndex(i-1, j+2, k-102) end local A = reshape(collect(1:9), (3,3)) - @test ind2sub(size(A), 6) == (3,2) - @test sub2ind(size(A), 3, 2) == 6 - @test ind2sub(A, 6) == (3,2) - @test sub2ind(A, 3, 2) == 6 + @test CartesianRange(size(A))[6] == CartesianIndex(3,2) + @test CartesianToLinear(size(A))[3, 2] == 6 + @test CartesianRange(A)[6] == CartesianIndex(3,2) + @test CartesianToLinear(A)[3, 2] == 6 + for i in 1:length(A) + @test CartesianToLinear(A)[CartesianRange(A)[i]] == i + end @testset "PR #9256" begin function pr9256() m = [1 2 3; 4 5 6; 7 8 9] - ind2sub(m, 6) + Base._ind2sub(m, 6) end @test pr9256() == (3,2) end @@ -616,10 +618,9 @@ function test_ind2sub(::Type{TestAbstractArray}) dims = tuple(rand(1:5, n)...) len = prod(dims) A = reshape(collect(1:len), dims...) - I = ind2sub(dims, [1:len...]) + I = CartesianRange(dims) for i in 1:len - idx = [ I[j][i] for j in 1:n ] - @test A[idx...] == A[i] + @test A[I[i]] == A[i] end end @@ -825,8 +826,8 @@ end @test Base.copymutable((1,2,3)) == [1,2,3] end -@testset "sub2ind for empty tuple" begin - @test sub2ind(()) == 1 +@testset "_sub2ind for empty tuple" begin + @test Base._sub2ind(()) == 1 end @testset "to_shape" begin @@ -896,5 +897,5 @@ end end @test CartesianRange(ones(2,3)) == CartesianRange((2,3)) - @test eachindex(CartesianRange((2,3))) == [1 3 5; 2 4 6] + @test CartesianToLinear((2,3)) == [1 3 5; 2 4 6] end diff --git a/test/arrayops.jl b/test/arrayops.jl index 226e71706d5a3..6bcfd04be6a22 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -1379,7 +1379,7 @@ end # issue #7197 function i7197() S = [1 2 3; 4 5 6; 7 8 9] - ind2sub(size(S), 5) + Base._ind2sub(size(S), 5) end @test i7197() == (2,2) diff --git a/test/euler.jl b/test/euler.jl index 52b9bd0a3702c..7e81d05b635a2 100644 --- a/test/euler.jl +++ b/test/euler.jl @@ -65,13 +65,14 @@ end #11: 70600674 function euler11(grid,n) m = typemin(eltype(grid)) + tolinear = CartesianToLinear(size(grid)) for i = n:size(grid,1)-n+1, j = n:size(grid,2)-n+1, di = -1:1, dj = -1:1 di == dj == 0 && continue - idx = sub2ind(size(grid), - di==0 ? fill(i,n) : range(i,di,n), - dj==0 ? fill(j,n) : range(j,dj,n)) + i_idxs = di==0 ? fill(i,n) : range(i,di,n) + j_idxs = dj==0 ? fill(j,n) : range(j,dj,n) + idx = tolinear[CartesianIndex.(i_idxs, j_idxs)] m = max(m,prod(grid[idx])) end return m