Skip to content

Commit

Permalink
Only try to call specialized findin method for sorted input when elem…
Browse files Browse the repository at this point in the history
…ents are Real. (#22010)

* Only try to call specialized findin method for sorted input when
elements are Real.

* Fix termination cirteria in _sortedfindin

Use total order `isless` for tests

Add tests

* Define eachindex for numbers

* Add a few comments to the algorithm
  • Loading branch information
andreasnoack authored May 23, 2017
1 parent 0b49fd5 commit cfa1999
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 10 deletions.
2 changes: 1 addition & 1 deletion base/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -753,7 +753,7 @@ next(A::AbstractArray, i) = (@_propagate_inbounds_meta; (idx, s) = next(i[1], i[
done(A::AbstractArray, i) = (@_propagate_inbounds_meta; done(i[1], i[2]))

# eachindex iterates over all indices. IndexCartesian definitions are later.
eachindex(A::AbstractVector) = (@_inline_meta(); indices1(A))
eachindex(A::Union{Number,AbstractVector}) = (@_inline_meta(); indices1(A))

"""
eachindex(A...)
Expand Down
31 changes: 22 additions & 9 deletions base/array.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1734,6 +1734,9 @@ function _findin(a, b)
ind
end

# If two collections are already sorted, findin can be computed with
# a single traversal of the two collections. This is much faster than
# using a hash table (although it has the same complexity).
function _sortedfindin(v, w)
viter, witer = eachindex(v), eachindex(w)
out = eltype(viter)[]
Expand All @@ -1745,23 +1748,30 @@ function _sortedfindin(v, w)
witerj, j = next(witer, j)
@inbounds begin
vi, wj = v[viteri], w[witerj]
while !(done(viter, i) || done(witer, j))
if vi < wj
while true
if isless(vi, wj)
if done(viter, i)
break
end
viteri, i = next(viter, i)
vi = v[viteri]
elseif vi > wj
elseif isless(wj, vi)
if done(witer, j)
break
end
witerj, j = next(witer, j)
wj = w[witerj]
else
push!(out, viteri)
if done(viter, i)
break
end
# We only increment the v iterator because v can have
# repeated matches to a single value in w
viteri, i = next(viter, i)
witerj, j = next(witer, j)
vi, wj = v[viteri], w[witerj]
vi = v[viteri]
end
end
if vi == wj
push!(out, viteri)
end
end
return out
end
Expand Down Expand Up @@ -1791,13 +1801,16 @@ julia> findin(a,b) # 10 is the only common element
4
```
"""
function findin(a, b)
function findin(a::Array{<:Real}, b::Union{Array{<:Real},Real})
if issorted(a, Sort.Forward) && issorted(b, Sort.Forward)
return _sortedfindin(a, b)
else
return _findin(a, b)
end
end
# issorted fails for some element types so the method above has to be restricted
# to element with isless/< defined.
findin(a, b) = _findin(a, b)

# Copying subregions
# TODO: DEPRECATE FOR #14770
Expand Down
14 changes: 14 additions & 0 deletions test/arrayops.jl
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,20 @@ end
@test findin(a, Int[]) == Int[]
@test findin(Int[], a) == Int[]

a = collect(1:3:15)
b = collect(2:4:10)
@test findin(a, b) == [4]
@test findin([a[1:4]; a[4:end]], b) == [4,5]

@test findin([1.0, NaN, 2.0], NaN) == [2]
@test findin([1.0, 2.0, NaN], NaN) == [3]

@testset "findin for uncomparable element types" begin
a = [1 + 1im, 1 - 1im]
@test findin(a, 1 + 1im) == [1]
@test findin(a, a) == [1,2]
end

rt = Base.return_types(setindex!, Tuple{Array{Int32, 3}, UInt8, Vector{Int}, Int16, UnitRange{Int}})
@test length(rt) == 1 && rt[1] == Array{Int32, 3}
end
Expand Down

0 comments on commit cfa1999

Please sign in to comment.