From 1a28512e1b6cd8aeb97b0de61756b860f36c57f2 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Sat, 8 Mar 2014 18:33:58 -0500 Subject: [PATCH] make ranges and arrays unequal, faster hashing of ranges, fixes #5778 fix == and isequal of ranges to only compare by components when it is safe to do so (ranges with different types but == components can generate different elements) add float32(x) etc. for FloatRange don't show floating point Range objects using colons, since those construct FloatRanges --- NEWS.md | 11 ++++++ base/abstractarray.jl | 7 ++++ base/bitarray.jl | 10 ----- base/range.jl | 33 +++++++++++++++- test/arrayops.jl | 22 +++++------ test/broadcast.jl | 2 +- test/ranges.jl | 88 +++++++++++++++++++++++++++---------------- 7 files changed, 116 insertions(+), 57 deletions(-) diff --git a/NEWS.md b/NEWS.md index c6462b408de58..bf23cdf871472 100644 --- a/NEWS.md +++ b/NEWS.md @@ -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 --------------------- @@ -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 ========================== diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 49e3b764e4502..6d51d0429731f 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -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 @@ -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 @@ -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 diff --git a/base/bitarray.jl b/base/bitarray.jl index 1cc388d8c8005..5d193ed8d2ae0 100644 --- a/base/bitarray.jl +++ b/base/bitarray.jl @@ -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 ## diff --git a/base/range.jl b/base/range.jl index 5230331889472..6f77542f574b8 100644 --- a/base/range.jl +++ b/base/range.jl @@ -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) @@ -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? diff --git a/test/arrayops.jl b/test/arrayops.jl index 29d370a7ad3e3..da711bebab8be 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -99,10 +99,10 @@ 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) @@ -110,19 +110,19 @@ sA[2:5:end] = -1 @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][:] @@ -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) @@ -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] diff --git a/test/broadcast.jl b/test/broadcast.jl index 9752a6e444a31..7e43596ad8d09 100644 --- a/test/broadcast.jl +++ b/test/broadcast.jl @@ -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] diff --git a/test/ranges.jl b/test/ranges.jl index 1870f4e8fe4e3..c897f1229a4bb 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -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 @@ -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