Skip to content

Commit 50c6b36

Browse files
authored
Merge pull request #446 from JuliaLang/nl/indices
Add CartesianIndices and LinearIndices
2 parents e7a2563 + 8768c35 commit 50c6b36

File tree

6 files changed

+149
-4
lines changed

6 files changed

+149
-4
lines changed

.travis.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ os:
33
- linux
44
- osx
55
julia:
6-
- 0.5
76
- 0.6
87
- nightly
98
notifications:

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,10 @@ Currently, the `@compat` macro supports the following syntaxes:
222222
* `replace` accepts a pair of pattern and replacement, with the number of replacements as
223223
a keyword argument ([#25165]).
224224

225+
* `CartesianIndices` and `LinearIndices` types represent cartesian and linear indices of
226+
an array (respectively), and indexing such objects allows translating from one kind of index
227+
to the other ([#25113]).
228+
225229
## Renaming
226230

227231

@@ -431,3 +435,4 @@ includes this fix. Find the minimum version from there.
431435
[#25162]: https://github.com/JuliaLang/julia/issues/25162
432436
[#25165]: https://github.com/JuliaLang/julia/issues/25165
433437
[#25168]: https://github.com/JuliaLang/julia/issues/25168
438+
[#25113]: https://github.com/JuliaLang/julia/issues/25113

REQUIRE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
julia 0.5
1+
julia 0.6

appveyor.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
environment:
22
matrix:
3-
- JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x86/0.5/julia-0.5-latest-win32.exe"
4-
- JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x64/0.5/julia-0.5-latest-win64.exe"
53
- JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x86/0.6/julia-0.6-latest-win32.exe"
64
- JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x64/0.6/julia-0.6-latest-win64.exe"
75
- JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x86/julia-latest-win32.exe"

src/Compat.jl

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,6 +1070,132 @@ end
10701070
replace(s, first(pat_rep), last(pat_rep), count)
10711071
end
10721072

1073+
@static if VERSION < v"0.7.0-DEV.3025"
1074+
import Base: convert, ndims, getindex, size, length, eltype,
1075+
start, next, done, first, last
1076+
export CartesianIndices, LinearIndices
1077+
1078+
struct CartesianIndices{N,R<:NTuple{N,AbstractUnitRange{Int}}} <: AbstractArray{CartesianIndex{N},N}
1079+
indices::R
1080+
end
1081+
1082+
CartesianIndices(::Tuple{}) = CartesianIndices{0,typeof(())}(())
1083+
CartesianIndices(inds::NTuple{N,AbstractUnitRange{Int}}) where {N} =
1084+
CartesianIndices{N,typeof(inds)}(inds)
1085+
CartesianIndices(inds::Vararg{AbstractUnitRange{Int},N}) where {N} =
1086+
CartesianIndices(inds)
1087+
CartesianIndices(inds::NTuple{N,AbstractUnitRange{<:Integer}}) where {N} =
1088+
CartesianIndices(map(r->convert(AbstractUnitRange{Int}, r), inds))
1089+
CartesianIndices(inds::Vararg{AbstractUnitRange{<:Integer},N}) where {N} =
1090+
CartesianIndices(inds)
1091+
1092+
CartesianIndices(index::CartesianIndex) = CartesianIndices(index.I)
1093+
CartesianIndices(sz::NTuple{N,<:Integer}) where {N} = CartesianIndices(map(Base.OneTo, sz))
1094+
CartesianIndices(inds::NTuple{N,Union{<:Integer,AbstractUnitRange{<:Integer}}}) where {N} =
1095+
CartesianIndices(map(i->first(i):last(i), inds))
1096+
1097+
CartesianIndices(A::AbstractArray) = CartesianIndices(axes(A))
1098+
1099+
convert(::Type{Tuple{}}, R::CartesianIndices{0}) = ()
1100+
convert(::Type{NTuple{N,AbstractUnitRange{Int}}}, R::CartesianIndices{N}) where {N} =
1101+
R.indices
1102+
1103+
convert(::Type{NTuple{N,AbstractUnitRange}}, R::CartesianIndices{N}) where {N} =
1104+
convert(NTuple{N,AbstractUnitRange{Int}}, R)
1105+
convert(::Type{NTuple{N,UnitRange{Int}}}, R::CartesianIndices{N}) where {N} =
1106+
UnitRange{Int}.(convert(NTuple{N,AbstractUnitRange}, R))
1107+
convert(::Type{NTuple{N,UnitRange}}, R::CartesianIndices{N}) where {N} =
1108+
UnitRange.(convert(NTuple{N,AbstractUnitRange}, R))
1109+
convert(::Type{Tuple{Vararg{AbstractUnitRange{Int}}}}, R::CartesianIndices{N}) where {N} =
1110+
convert(NTuple{N,AbstractUnitRange{Int}}, R)
1111+
convert(::Type{Tuple{Vararg{AbstractUnitRange}}}, R::CartesianIndices) =
1112+
convert(Tuple{Vararg{AbstractUnitRange{Int}}}, R)
1113+
convert(::Type{Tuple{Vararg{UnitRange{Int}}}}, R::CartesianIndices{N}) where {N} =
1114+
convert(NTuple{N,UnitRange{Int}}, R)
1115+
convert(::Type{Tuple{Vararg{UnitRange}}}, R::CartesianIndices) =
1116+
convert(Tuple{Vararg{UnitRange{Int}}}, R)
1117+
1118+
# AbstractArray implementation
1119+
Base.IndexStyle(::Type{CartesianIndices{N,R}}) where {N,R} = IndexCartesian()
1120+
@inline Base.getindex(iter::CartesianIndices{N,R}, I::Vararg{Int, N}) where {N,R} = CartesianIndex(first.(iter.indices) .- 1 .+ I)
1121+
1122+
ndims(R::CartesianIndices) = ndims(typeof(R))
1123+
ndims(::Type{CartesianIndices{N}}) where {N} = N
1124+
ndims(::Type{CartesianIndices{N,TT}}) where {N,TT} = N
1125+
1126+
eltype(R::CartesianIndices) = eltype(typeof(R))
1127+
eltype(::Type{CartesianIndices{N}}) where {N} = CartesianIndex{N}
1128+
eltype(::Type{CartesianIndices{N,TT}}) where {N,TT} = CartesianIndex{N}
1129+
Base.iteratorsize(::Type{<:CartesianIndices}) = Base.HasShape()
1130+
1131+
@inline function start(iter::CartesianIndices)
1132+
iterfirst, iterlast = first(iter), last(iter)
1133+
if any(map(>, iterfirst.I, iterlast.I))
1134+
return iterlast+1
1135+
end
1136+
iterfirst
1137+
end
1138+
@inline function next(iter::CartesianIndices, state)
1139+
state, CartesianIndex(inc(state.I, first(iter).I, last(iter).I))
1140+
end
1141+
# increment & carry
1142+
@inline inc(::Tuple{}, ::Tuple{}, ::Tuple{}) = ()
1143+
@inline inc(state::Tuple{Int}, start::Tuple{Int}, stop::Tuple{Int}) = (state[1]+1,)
1144+
@inline function inc(state, start, stop)
1145+
if state[1] < stop[1]
1146+
return (state[1]+1,Base.tail(state)...)
1147+
end
1148+
newtail = inc(Base.tail(state), Base.tail(start), Base.tail(stop))
1149+
(start[1], newtail...)
1150+
end
1151+
@inline done(iter::CartesianIndices, state) = state.I[end] > last(iter.indices[end])
1152+
1153+
# 0-d cartesian ranges are special-cased to iterate once and only once
1154+
start(iter::CartesianIndices{0}) = false
1155+
next(iter::CartesianIndices{0}, state) = CartesianIndex(), true
1156+
done(iter::CartesianIndices{0}, state) = state
1157+
1158+
size(iter::CartesianIndices) = map(dimlength, first(iter).I, last(iter).I)
1159+
dimlength(start, stop) = stop-start+1
1160+
1161+
length(iter::CartesianIndices) = prod(size(iter))
1162+
1163+
first(iter::CartesianIndices) = CartesianIndex(map(first, iter.indices))
1164+
last(iter::CartesianIndices) = CartesianIndex(map(last, iter.indices))
1165+
1166+
@inline function in(i::CartesianIndex{N}, r::CartesianIndices{N}) where {N}
1167+
_in(true, i.I, first(r).I, last(r).I)
1168+
end
1169+
_in(b, ::Tuple{}, ::Tuple{}, ::Tuple{}) = b
1170+
@inline _in(b, i, start, stop) = _in(b & (start[1] <= i[1] <= stop[1]), tail(i), tail(start), tail(stop))
1171+
1172+
struct LinearIndices{N,R<:NTuple{N,AbstractUnitRange{Int}}} <: AbstractArray{Int,N}
1173+
indices::R
1174+
end
1175+
1176+
LinearIndices(inds::CartesianIndices{N,R}) where {N,R} = LinearIndices{N,R}(inds.indices)
1177+
LinearIndices(::Tuple{}) = LinearIndices(CartesianIndices(()))
1178+
LinearIndices(inds::NTuple{N,AbstractUnitRange{Int}}) where {N} = LinearIndices(CartesianIndices(inds))
1179+
LinearIndices(inds::Vararg{AbstractUnitRange{Int},N}) where {N} = LinearIndices(CartesianIndices(inds))
1180+
LinearIndices(inds::NTuple{N,AbstractUnitRange{<:Integer}}) where {N} = LinearIndices(CartesianIndices(inds))
1181+
LinearIndices(inds::Vararg{AbstractUnitRange{<:Integer},N}) where {N} = LinearIndices(CartesianIndices(inds))
1182+
LinearIndices(index::CartesianIndex) = LinearIndices(CartesianIndices(index))
1183+
LinearIndices(sz::NTuple{N,<:Integer}) where {N} = LinearIndices(CartesianIndices(sz))
1184+
LinearIndices(inds::NTuple{N,Union{<:Integer,AbstractUnitRange{<:Integer}}}) where {N} = LinearIndices(CartesianIndices(inds))
1185+
LinearIndices(A::AbstractArray) = LinearIndices(CartesianIndices(A))
1186+
1187+
# AbstractArray implementation
1188+
Base.IndexStyle(::Type{LinearIndices{N,R}}) where {N,R} = IndexCartesian()
1189+
Compat.axes(iter::LinearIndices{N,R}) where {N,R} = iter.indices
1190+
@inline function Base.getindex(iter::LinearIndices{N,R}, I::Vararg{Int, N}) where {N,R}
1191+
dims = length.(iter.indices)
1192+
#without the inbounds, this is slower than Base._sub2ind(iter.indices, I...)
1193+
@inbounds result = reshape(1:prod(dims), dims)[(I .- first.(iter.indices) .+ 1)...]
1194+
return result
1195+
end
1196+
end
1197+
1198+
10731199
include("deprecated.jl")
10741200

10751201
end # module Compat

test/runtests.jl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,6 +1076,23 @@ end
10761076
@test replace("abcb", "b"=>"c") == "accc"
10771077
@test replace("abcb", "b"=>"c", count=1) == "accb"
10781078

1079+
# 0.7.0-DEV.3025
1080+
let c = CartesianIndices(1:3, 1:2), l = LinearIndices(1:3, 1:2)
1081+
@test LinearIndices(c) == collect(l)
1082+
@test CartesianIndices(l) == collect(c)
1083+
@test first(c) == CartesianIndex(1, 1)
1084+
@test CartesianIndex(1, 1) in c
1085+
@test first(l) == 1
1086+
@test size(c) == (3, 2)
1087+
@test c == collect(c) == [CartesianIndex(1, 1) CartesianIndex(1, 2)
1088+
CartesianIndex(2, 1) CartesianIndex(2, 2)
1089+
CartesianIndex(3, 1) CartesianIndex(3, 2)]
1090+
@test l == collect(l) == reshape(1:6, 3, 2)
1091+
@test c[1:6] == vec(c)
1092+
@test l == l[c] == map(i -> l[i], c)
1093+
@test l[vec(c)] == collect(1:6)
1094+
end
1095+
10791096
if VERSION < v"0.6.0"
10801097
include("deprecated.jl")
10811098
end

0 commit comments

Comments
 (0)