Skip to content

Commit

Permalink
Merge pull request #6084 from JuliaLang/jb/equalranges
Browse files Browse the repository at this point in the history
make ranges and arrays unequal, faster hashing of ranges, fixes #5778
  • Loading branch information
JeffBezanson committed Mar 11, 2014
2 parents e2515bb + 1a28512 commit 6128592
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 57 deletions.
11 changes: 11 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,9 @@ Library improvements
* Constructors for collections (`Set`, `Dict`, etc.) now generally accept a
single iterable argument giving the elements of the collection ([#4996], [#4871])

* Ranges and arrays with the same elements are now unequal. This allows hashing
and comparing ranges to be faster. ([#5778])

Deprecated or removed
---------------------

Expand Down Expand Up @@ -307,6 +310,14 @@ Deprecated or removed
[#2333]: https://github.com/JuliaLang/julia/issues/2333
[#5636]: https://github.com/JuliaLang/julia/issues/5636
[#1268]: https://github.com/JuliaLang/julia/issues/1268
[#5677]: https://github.com/JuliaLang/julia/issues/5677
[#5545]: https://github.com/JuliaLang/julia/issues/5545
[#6057]: https://github.com/JuliaLang/julia/issues/6057
[#6056]: https://github.com/JuliaLang/julia/issues/6056
[#3344]: https://github.com/JuliaLang/julia/issues/3344
[#5737]: https://github.com/JuliaLang/julia/issues/5737
[#6073]: https://github.com/JuliaLang/julia/issues/6073
[#5778]: https://github.com/JuliaLang/julia/issues/5778

Julia v0.2.0 Release Notes
==========================
Expand Down
7 changes: 7 additions & 0 deletions base/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ for fn in _numeric_conversion_func_names
@eval begin
$fn(r::Range ) = Range($fn(r.start), $fn(r.step), r.len)
$fn(r::Range1) = Range1($fn(r.start), r.len)
$fn(r::FloatRange) = FloatRange($fn(r.start), $fn(r.step), r.len, $fn(r.divisor))
end
end

Expand Down Expand Up @@ -813,6 +814,9 @@ function isequal(A::AbstractArray, B::AbstractArray)
if size(A) != size(B)
return false
end
if isa(A,Ranges) != isa(B,Ranges)
return false
end
for i = 1:length(A)
if !isequal(A[i], B[i])
return false
Expand All @@ -834,6 +838,9 @@ function (==)(A::AbstractArray, B::AbstractArray)
if size(A) != size(B)
return false
end
if isa(A,Ranges) != isa(B,Ranges)
return false
end
for i = 1:length(A)
if !(A[i]==B[i])
return false
Expand Down
10 changes: 0 additions & 10 deletions base/bitarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1108,16 +1108,6 @@ function (!=)(A::BitArray, B::BitArray)
return A.chunks != B.chunks
end

# TODO: avoid bitpack/bitunpack
for f in (:(==), :!=)
@eval begin
($f)(A::BitArray, B::AbstractArray{Bool}) = ($f)(A, bitpack(B))
($f)(A::AbstractArray{Bool}, B::BitArray) = ($f)(bitpack(A), B)
($f)(A::BitArray, B::AbstractArray) = ($f)(bitunpack(A), B)
($f)(A::AbstractArray, B::BitArray) = ($f)(A, bitunpack(B))
end
end


## Data movement ##

Expand Down
33 changes: 31 additions & 2 deletions base/range.jl
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,8 @@ function show(io::IO, r::Ranges)
end
show(io::IO, r::Range1) = print(io, repr(first(r)), ':', repr(last(r)))

show{T<:FloatingPoint}(io::IO, r::Range{T}) = invoke(show, (IO,Any), io, r)

start(r::Ranges) = 0
next{T}(r::Range{T}, i) = (oftype(T, r.start + i*step(r)), i+1)
next{T}(r::Range1{T}, i) = (oftype(T, r.start + i), i+1)
Expand All @@ -234,8 +236,35 @@ start{T<:Integer}(r::Range1{T}) = r.start
next{T<:Integer}(r::Range1{T}, i) = (i, oftype(T, i+1))
done{T<:Integer}(r::Range1{T}, i) = i==oftype(T, r.start+r.len)

==(r::Ranges, s::Ranges) = (first(r)==first(s)) & (step(r)==step(s)) & (length(r)==length(s))
==(r::Range1, s::Range1) = (r.start==s.start) & (r.len==s.len)
isequal{T<:Ranges}(r::T, s::T) =
(first(r)==first(s)) & (step(r)==step(s)) & (length(r)==length(s))

isequal(r::Ranges, s::Ranges) = false

=={T<:Ranges}(r::T, s::T) = isequal(r, s)

=={T<:Integer, S<:Integer}(r::Ranges{T}, s::Ranges{S}) =
(first(r)==first(s)) & (step(r)==step(s)) & (length(r)==length(s))

function ==(r::Ranges, s::Ranges)
lr = length(r)
if lr != length(s)
return false
end
u, v = start(r), start(s)
while !done(r, u)
x, u = next(r, u)
y, v = next(s, v)
if x != y
return false
end
end
return true
end

# hashing ranges by component at worst leads to collisions for very similar ranges
hash(r::Ranges) =
bitmix(hash(first(r)), bitmix(hash(step(r)), bitmix(hash(length(r)), uint(0xaaeeaaee))))

# TODO: isless?

Expand Down
22 changes: 11 additions & 11 deletions test/arrayops.jl
Original file line number Diff line number Diff line change
Expand Up @@ -99,30 +99,30 @@ A = reshape(1:120, 3, 5, 8)
sA = sub(A, 2, 1:5, :)
@test parent(sA) == A
@test parentindexes(sA) == (2:2, 1:5, 1:8)
@test Base.parentdims(sA) == 1:3
@test Base.parentdims(sA) == [1:3]
@test size(sA) == (1, 5, 8)
@test_throws sA[2, 1:8]
@test sA[1, 2, 1:8][:] == 5:15:120
@test sA[1, 2, 1:8][:] == [5:15:120]
sA[2:5:end] = -1
@test all(sA[2:5:end] .== -1)
@test all(A[5:15:120] .== -1)
@test strides(sA) == (1,3,15)
@test stride(sA,3) == 15
@test stride(sA,4) == 120
sA = sub(A, 1:3, 1:5, 5)
@test Base.parentdims(sA) == 1:2
@test Base.parentdims(sA) == [1:2]
sA[1:3,1:5] = -2
@test all(A[:,:,5] .== -2)
sA[:] = -3
@test all(A[:,:,5] .== -3)
@test strides(sA) == (1,3)
sA = sub(A, 1:3, 3, 2:5)
@test Base.parentdims(sA) == 1:3
@test Base.parentdims(sA) == [1:3]
@test size(sA) == (3,1,4)
@test sA == A[1:3,3,2:5]
@test sA[:] == A[1:3,3,2:5][:]
sA = sub(A, 1:2:3, 1:3:5, 1:2:8)
@test Base.parentdims(sA) == 1:3
@test Base.parentdims(sA) == [1:3]
@test strides(sA) == (2,9,30)
@test sA[:] == A[1:2:3, 1:3:5, 1:2:8][:]

Expand All @@ -138,17 +138,17 @@ A = reshape(1:120, 3, 5, 8)
sA = slice(A, 2, :, 1:8)
@test parent(sA) == A
@test parentindexes(sA) == (2, 1:5, 1:8)
@test Base.parentdims(sA) == 2:3
@test Base.parentdims(sA) == [2:3]
@test size(sA) == (5, 8)
@test strides(sA) == (3,15)
@test sA[2, 1:8][:] == 5:15:120
@test sA[:,1] == 2:3:14
@test sA[2:5:end] == 5:15:110
@test sA[2, 1:8][:] == [5:15:120]
@test sA[:,1] == [2:3:14]
@test sA[2:5:end] == [5:15:110]
sA[2:5:end] = -1
@test all(sA[2:5:end] .== -1)
@test all(A[5:15:120] .== -1)
sA = slice(A, 1:3, 1:5, 5)
@test Base.parentdims(sA) == 1:2
@test Base.parentdims(sA) == [1:2]
@test size(sA) == (3,5)
@test strides(sA) == (1,3)
sA = slice(A, 1:2:3, 3, 1:2:8)
Expand Down Expand Up @@ -198,7 +198,7 @@ let
X = get(A, -5:5, nan(Float32))
@test eltype(X) == Float32
@test isnan(X) == [trues(6),falses(5)]
@test X[7:11] == 1:5
@test X[7:11] == [1:5]
X = get(A, (2:4, 9:-2:-13), 0)
Xv = zeros(Int, 3, 12)
Xv[1:2, 2:5] = A[2:3, 7:-2:1]
Expand Down
2 changes: 1 addition & 1 deletion test/broadcast.jl
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ end
r1 = 1:1
r2 = 1:5
ratio = [1,1/2,1/3,1/4,1/5]
@test r1.*r2 == 1:5
@test r1.*r2 == [1:5]
@test r1./r2 == ratio
m = [1:2]'
@test m.*r2 == [1:5 2:2:10]
Expand Down
88 changes: 55 additions & 33 deletions test/ranges.jl
Original file line number Diff line number Diff line change
Expand Up @@ -168,39 +168,39 @@ end

# tricky floating-point ranges

@test 0.1:0.1:0.3 == [1:3]./10
@test 0.0:0.1:0.3 == [0:3]./10
@test 0.3:-0.1:-0.1 == [3:-1:-1]./10
@test 0.1:-0.1:-0.3 == [1:-1:-3]./10
@test 0.0:0.1:1.0 == [0:10]./10
@test 0.0:-0.1:1.0 == []
@test 0.0:0.1:-1.0 == []
@test 0.0:-0.1:-1.0 == [0:-1:-10]./10
@test 1.0:1/49:27.0 == [49:1323]./49
@test 0.0:0.7:2.1 == [0:7:21]./10
@test 0.0:1.1:3.3 == [0:11:33]./10
@test 0.1:1.1:3.4 == [1:11:34]./10
@test 0.0:1.3:3.9 == [0:13:39]./10
@test 0.1:1.3:4.0 == [1:13:40]./10
@test 1.1:1.1:3.3 == [11:11:33]./10
@test 0.3:0.1:1.1 == [3:1:11]./10

@test 0.0:1.0:5.5 == [0:10:55]./10
@test 0.0:-1.0:0.5 == []
@test 0.0:1.0:0.5 == [0.0]

@test prevfloat(0.1):0.1:0.3 == [prevfloat(0.1), 0.2, 0.3]
@test nextfloat(0.1):0.1:0.3 == [nextfloat(0.1), 0.2]
@test prevfloat(0.0):0.1:0.3 == [prevfloat(0.0), 0.1, 0.2]
@test nextfloat(0.0):0.1:0.3 == [nextfloat(0.0), 0.1, 0.2]
@test 0.1:0.1:prevfloat(0.3) == [0.1, 0.2]
@test 0.1:0.1:nextfloat(0.3) == [0.1, 0.2, nextfloat(0.3)]
@test 0.0:0.1:prevfloat(0.3) == [0.0, 0.1, 0.2]
@test 0.0:0.1:nextfloat(0.3) == [0.0, 0.1, 0.2, nextfloat(0.3)]
@test 0.1:prevfloat(0.1):0.3 == [0.1, 0.2, 0.3]
@test 0.1:nextfloat(0.1):0.3 == [0.1, 0.2]
@test 0.0:prevfloat(0.1):0.3 == [0.0, prevfloat(0.1), prevfloat(0.2), 0.3]
@test 0.0:nextfloat(0.1):0.3 == [0.0, nextfloat(0.1), nextfloat(0.2)]
@test [0.1:0.1:0.3] == [1:3]./10
@test [0.0:0.1:0.3] == [0:3]./10
@test [0.3:-0.1:-0.1] == [3:-1:-1]./10
@test [0.1:-0.1:-0.3] == [1:-1:-3]./10
@test [0.0:0.1:1.0] == [0:10]./10
@test [0.0:-0.1:1.0] == []
@test [0.0:0.1:-1.0] == []
@test [0.0:-0.1:-1.0] == [0:-1:-10]./10
@test [1.0:1/49:27.0] == [49:1323]./49
@test [0.0:0.7:2.1] == [0:7:21]./10
@test [0.0:1.1:3.3] == [0:11:33]./10
@test [0.1:1.1:3.4] == [1:11:34]./10
@test [0.0:1.3:3.9] == [0:13:39]./10
@test [0.1:1.3:4.0] == [1:13:40]./10
@test [1.1:1.1:3.3] == [11:11:33]./10
@test [0.3:0.1:1.1] == [3:1:11]./10

@test [0.0:1.0:5.5] == [0:10:55]./10
@test [0.0:-1.0:0.5] == []
@test [0.0:1.0:0.5] == [0.0]

@test [prevfloat(0.1):0.1:0.3] == [prevfloat(0.1), 0.2, 0.3]
@test [nextfloat(0.1):0.1:0.3] == [nextfloat(0.1), 0.2]
@test [prevfloat(0.0):0.1:0.3] == [prevfloat(0.0), 0.1, 0.2]
@test [nextfloat(0.0):0.1:0.3] == [nextfloat(0.0), 0.1, 0.2]
@test [0.1:0.1:prevfloat(0.3)] == [0.1, 0.2]
@test [0.1:0.1:nextfloat(0.3)] == [0.1, 0.2, nextfloat(0.3)]
@test [0.0:0.1:prevfloat(0.3)] == [0.0, 0.1, 0.2]
@test [0.0:0.1:nextfloat(0.3)] == [0.0, 0.1, 0.2, nextfloat(0.3)]
@test [0.1:prevfloat(0.1):0.3] == [0.1, 0.2, 0.3]
@test [0.1:nextfloat(0.1):0.3] == [0.1, 0.2]
@test [0.0:prevfloat(0.1):0.3] == [0.0, prevfloat(0.1), prevfloat(0.2), 0.3]
@test [0.0:nextfloat(0.1):0.3] == [0.0, nextfloat(0.1), nextfloat(0.2)]

for T = (Float32, Float64,),# BigFloat),
a = -5:25, s = [-5:-1;1:25], d = 1:25, n = -1:15
Expand All @@ -210,3 +210,25 @@ for T = (Float32, Float64,),# BigFloat),
stop = convert(T,(a+(n-1)*s))/den
@test [start:step:stop] == T[a:s:a+(n-1)*s]./den
end

# near-equal ranges
@test 0.0:0.1:1.0 != Range(0.0,0.1,11)

# comparing and hashing ranges
let
Rs = {1:2, int32(1:3:17), int64(1:3:17), 1:0, 17:-3:0,
0.0:0.1:1.0, Range(0.0,0.1,11),
float32(0.0:0.1:1.0), float32(Range(0.0,0.1,11))}
for r in Rs
ar = collect(r)
@test r != ar
@test !isequal(r, ar)
for s in Rs
as = collect(s)

@test !isequal(r, s) || hash(r)==hash(s)

@test (r==s) == (ar==as)
end
end
end

0 comments on commit 6128592

Please sign in to comment.