Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make ranges and arrays unequal, faster hashing of ranges, fixes #5778 #6084

Merged
merged 1 commit into from
Mar 11, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,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 @@ -303,6 +306,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 @@ -306,6 +306,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 @@ -814,6 +815,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 @@ -835,6 +839,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 @@ -1226,16 +1226,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 @@ -62,7 +62,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