Skip to content

Commit 6128592

Browse files
committed
Merge pull request #6084 from JuliaLang/jb/equalranges
make ranges and arrays unequal, faster hashing of ranges, fixes #5778
2 parents e2515bb + 1a28512 commit 6128592

File tree

7 files changed

+116
-57
lines changed

7 files changed

+116
-57
lines changed

NEWS.md

+11
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,9 @@ Library improvements
214214
* Constructors for collections (`Set`, `Dict`, etc.) now generally accept a
215215
single iterable argument giving the elements of the collection ([#4996], [#4871])
216216

217+
* Ranges and arrays with the same elements are now unequal. This allows hashing
218+
and comparing ranges to be faster. ([#5778])
219+
217220
Deprecated or removed
218221
---------------------
219222

@@ -307,6 +310,14 @@ Deprecated or removed
307310
[#2333]: https://github.com/JuliaLang/julia/issues/2333
308311
[#5636]: https://github.com/JuliaLang/julia/issues/5636
309312
[#1268]: https://github.com/JuliaLang/julia/issues/1268
313+
[#5677]: https://github.com/JuliaLang/julia/issues/5677
314+
[#5545]: https://github.com/JuliaLang/julia/issues/5545
315+
[#6057]: https://github.com/JuliaLang/julia/issues/6057
316+
[#6056]: https://github.com/JuliaLang/julia/issues/6056
317+
[#3344]: https://github.com/JuliaLang/julia/issues/3344
318+
[#5737]: https://github.com/JuliaLang/julia/issues/5737
319+
[#6073]: https://github.com/JuliaLang/julia/issues/6073
320+
[#5778]: https://github.com/JuliaLang/julia/issues/5778
310321

311322
Julia v0.2.0 Release Notes
312323
==========================

base/abstractarray.jl

+7
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,7 @@ for fn in _numeric_conversion_func_names
307307
@eval begin
308308
$fn(r::Range ) = Range($fn(r.start), $fn(r.step), r.len)
309309
$fn(r::Range1) = Range1($fn(r.start), r.len)
310+
$fn(r::FloatRange) = FloatRange($fn(r.start), $fn(r.step), r.len, $fn(r.divisor))
310311
end
311312
end
312313

@@ -813,6 +814,9 @@ function isequal(A::AbstractArray, B::AbstractArray)
813814
if size(A) != size(B)
814815
return false
815816
end
817+
if isa(A,Ranges) != isa(B,Ranges)
818+
return false
819+
end
816820
for i = 1:length(A)
817821
if !isequal(A[i], B[i])
818822
return false
@@ -834,6 +838,9 @@ function (==)(A::AbstractArray, B::AbstractArray)
834838
if size(A) != size(B)
835839
return false
836840
end
841+
if isa(A,Ranges) != isa(B,Ranges)
842+
return false
843+
end
837844
for i = 1:length(A)
838845
if !(A[i]==B[i])
839846
return false

base/bitarray.jl

-10
Original file line numberDiff line numberDiff line change
@@ -1108,16 +1108,6 @@ function (!=)(A::BitArray, B::BitArray)
11081108
return A.chunks != B.chunks
11091109
end
11101110

1111-
# TODO: avoid bitpack/bitunpack
1112-
for f in (:(==), :!=)
1113-
@eval begin
1114-
($f)(A::BitArray, B::AbstractArray{Bool}) = ($f)(A, bitpack(B))
1115-
($f)(A::AbstractArray{Bool}, B::BitArray) = ($f)(bitpack(A), B)
1116-
($f)(A::BitArray, B::AbstractArray) = ($f)(bitunpack(A), B)
1117-
($f)(A::AbstractArray, B::BitArray) = ($f)(A, bitunpack(B))
1118-
end
1119-
end
1120-
11211111

11221112
## Data movement ##
11231113

base/range.jl

+31-2
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,8 @@ function show(io::IO, r::Ranges)
222222
end
223223
show(io::IO, r::Range1) = print(io, repr(first(r)), ':', repr(last(r)))
224224

225+
show{T<:FloatingPoint}(io::IO, r::Range{T}) = invoke(show, (IO,Any), io, r)
226+
225227
start(r::Ranges) = 0
226228
next{T}(r::Range{T}, i) = (oftype(T, r.start + i*step(r)), i+1)
227229
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
234236
next{T<:Integer}(r::Range1{T}, i) = (i, oftype(T, i+1))
235237
done{T<:Integer}(r::Range1{T}, i) = i==oftype(T, r.start+r.len)
236238

237-
==(r::Ranges, s::Ranges) = (first(r)==first(s)) & (step(r)==step(s)) & (length(r)==length(s))
238-
==(r::Range1, s::Range1) = (r.start==s.start) & (r.len==s.len)
239+
isequal{T<:Ranges}(r::T, s::T) =
240+
(first(r)==first(s)) & (step(r)==step(s)) & (length(r)==length(s))
241+
242+
isequal(r::Ranges, s::Ranges) = false
243+
244+
=={T<:Ranges}(r::T, s::T) = isequal(r, s)
245+
246+
=={T<:Integer, S<:Integer}(r::Ranges{T}, s::Ranges{S}) =
247+
(first(r)==first(s)) & (step(r)==step(s)) & (length(r)==length(s))
248+
249+
function ==(r::Ranges, s::Ranges)
250+
lr = length(r)
251+
if lr != length(s)
252+
return false
253+
end
254+
u, v = start(r), start(s)
255+
while !done(r, u)
256+
x, u = next(r, u)
257+
y, v = next(s, v)
258+
if x != y
259+
return false
260+
end
261+
end
262+
return true
263+
end
264+
265+
# hashing ranges by component at worst leads to collisions for very similar ranges
266+
hash(r::Ranges) =
267+
bitmix(hash(first(r)), bitmix(hash(step(r)), bitmix(hash(length(r)), uint(0xaaeeaaee))))
239268

240269
# TODO: isless?
241270

test/arrayops.jl

+11-11
Original file line numberDiff line numberDiff line change
@@ -99,30 +99,30 @@ A = reshape(1:120, 3, 5, 8)
9999
sA = sub(A, 2, 1:5, :)
100100
@test parent(sA) == A
101101
@test parentindexes(sA) == (2:2, 1:5, 1:8)
102-
@test Base.parentdims(sA) == 1:3
102+
@test Base.parentdims(sA) == [1:3]
103103
@test size(sA) == (1, 5, 8)
104104
@test_throws sA[2, 1:8]
105-
@test sA[1, 2, 1:8][:] == 5:15:120
105+
@test sA[1, 2, 1:8][:] == [5:15:120]
106106
sA[2:5:end] = -1
107107
@test all(sA[2:5:end] .== -1)
108108
@test all(A[5:15:120] .== -1)
109109
@test strides(sA) == (1,3,15)
110110
@test stride(sA,3) == 15
111111
@test stride(sA,4) == 120
112112
sA = sub(A, 1:3, 1:5, 5)
113-
@test Base.parentdims(sA) == 1:2
113+
@test Base.parentdims(sA) == [1:2]
114114
sA[1:3,1:5] = -2
115115
@test all(A[:,:,5] .== -2)
116116
sA[:] = -3
117117
@test all(A[:,:,5] .== -3)
118118
@test strides(sA) == (1,3)
119119
sA = sub(A, 1:3, 3, 2:5)
120-
@test Base.parentdims(sA) == 1:3
120+
@test Base.parentdims(sA) == [1:3]
121121
@test size(sA) == (3,1,4)
122122
@test sA == A[1:3,3,2:5]
123123
@test sA[:] == A[1:3,3,2:5][:]
124124
sA = sub(A, 1:2:3, 1:3:5, 1:2:8)
125-
@test Base.parentdims(sA) == 1:3
125+
@test Base.parentdims(sA) == [1:3]
126126
@test strides(sA) == (2,9,30)
127127
@test sA[:] == A[1:2:3, 1:3:5, 1:2:8][:]
128128

@@ -138,17 +138,17 @@ A = reshape(1:120, 3, 5, 8)
138138
sA = slice(A, 2, :, 1:8)
139139
@test parent(sA) == A
140140
@test parentindexes(sA) == (2, 1:5, 1:8)
141-
@test Base.parentdims(sA) == 2:3
141+
@test Base.parentdims(sA) == [2:3]
142142
@test size(sA) == (5, 8)
143143
@test strides(sA) == (3,15)
144-
@test sA[2, 1:8][:] == 5:15:120
145-
@test sA[:,1] == 2:3:14
146-
@test sA[2:5:end] == 5:15:110
144+
@test sA[2, 1:8][:] == [5:15:120]
145+
@test sA[:,1] == [2:3:14]
146+
@test sA[2:5:end] == [5:15:110]
147147
sA[2:5:end] = -1
148148
@test all(sA[2:5:end] .== -1)
149149
@test all(A[5:15:120] .== -1)
150150
sA = slice(A, 1:3, 1:5, 5)
151-
@test Base.parentdims(sA) == 1:2
151+
@test Base.parentdims(sA) == [1:2]
152152
@test size(sA) == (3,5)
153153
@test strides(sA) == (1,3)
154154
sA = slice(A, 1:2:3, 3, 1:2:8)
@@ -198,7 +198,7 @@ let
198198
X = get(A, -5:5, nan(Float32))
199199
@test eltype(X) == Float32
200200
@test isnan(X) == [trues(6),falses(5)]
201-
@test X[7:11] == 1:5
201+
@test X[7:11] == [1:5]
202202
X = get(A, (2:4, 9:-2:-13), 0)
203203
Xv = zeros(Int, 3, 12)
204204
Xv[1:2, 2:5] = A[2:3, 7:-2:1]

test/broadcast.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ end
9898
r1 = 1:1
9999
r2 = 1:5
100100
ratio = [1,1/2,1/3,1/4,1/5]
101-
@test r1.*r2 == 1:5
101+
@test r1.*r2 == [1:5]
102102
@test r1./r2 == ratio
103103
m = [1:2]'
104104
@test m.*r2 == [1:5 2:2:10]

test/ranges.jl

+55-33
Original file line numberDiff line numberDiff line change
@@ -168,39 +168,39 @@ end
168168

169169
# tricky floating-point ranges
170170

171-
@test 0.1:0.1:0.3 == [1:3]./10
172-
@test 0.0:0.1:0.3 == [0:3]./10
173-
@test 0.3:-0.1:-0.1 == [3:-1:-1]./10
174-
@test 0.1:-0.1:-0.3 == [1:-1:-3]./10
175-
@test 0.0:0.1:1.0 == [0:10]./10
176-
@test 0.0:-0.1:1.0 == []
177-
@test 0.0:0.1:-1.0 == []
178-
@test 0.0:-0.1:-1.0 == [0:-1:-10]./10
179-
@test 1.0:1/49:27.0 == [49:1323]./49
180-
@test 0.0:0.7:2.1 == [0:7:21]./10
181-
@test 0.0:1.1:3.3 == [0:11:33]./10
182-
@test 0.1:1.1:3.4 == [1:11:34]./10
183-
@test 0.0:1.3:3.9 == [0:13:39]./10
184-
@test 0.1:1.3:4.0 == [1:13:40]./10
185-
@test 1.1:1.1:3.3 == [11:11:33]./10
186-
@test 0.3:0.1:1.1 == [3:1:11]./10
187-
188-
@test 0.0:1.0:5.5 == [0:10:55]./10
189-
@test 0.0:-1.0:0.5 == []
190-
@test 0.0:1.0:0.5 == [0.0]
191-
192-
@test prevfloat(0.1):0.1:0.3 == [prevfloat(0.1), 0.2, 0.3]
193-
@test nextfloat(0.1):0.1:0.3 == [nextfloat(0.1), 0.2]
194-
@test prevfloat(0.0):0.1:0.3 == [prevfloat(0.0), 0.1, 0.2]
195-
@test nextfloat(0.0):0.1:0.3 == [nextfloat(0.0), 0.1, 0.2]
196-
@test 0.1:0.1:prevfloat(0.3) == [0.1, 0.2]
197-
@test 0.1:0.1:nextfloat(0.3) == [0.1, 0.2, nextfloat(0.3)]
198-
@test 0.0:0.1:prevfloat(0.3) == [0.0, 0.1, 0.2]
199-
@test 0.0:0.1:nextfloat(0.3) == [0.0, 0.1, 0.2, nextfloat(0.3)]
200-
@test 0.1:prevfloat(0.1):0.3 == [0.1, 0.2, 0.3]
201-
@test 0.1:nextfloat(0.1):0.3 == [0.1, 0.2]
202-
@test 0.0:prevfloat(0.1):0.3 == [0.0, prevfloat(0.1), prevfloat(0.2), 0.3]
203-
@test 0.0:nextfloat(0.1):0.3 == [0.0, nextfloat(0.1), nextfloat(0.2)]
171+
@test [0.1:0.1:0.3] == [1:3]./10
172+
@test [0.0:0.1:0.3] == [0:3]./10
173+
@test [0.3:-0.1:-0.1] == [3:-1:-1]./10
174+
@test [0.1:-0.1:-0.3] == [1:-1:-3]./10
175+
@test [0.0:0.1:1.0] == [0:10]./10
176+
@test [0.0:-0.1:1.0] == []
177+
@test [0.0:0.1:-1.0] == []
178+
@test [0.0:-0.1:-1.0] == [0:-1:-10]./10
179+
@test [1.0:1/49:27.0] == [49:1323]./49
180+
@test [0.0:0.7:2.1] == [0:7:21]./10
181+
@test [0.0:1.1:3.3] == [0:11:33]./10
182+
@test [0.1:1.1:3.4] == [1:11:34]./10
183+
@test [0.0:1.3:3.9] == [0:13:39]./10
184+
@test [0.1:1.3:4.0] == [1:13:40]./10
185+
@test [1.1:1.1:3.3] == [11:11:33]./10
186+
@test [0.3:0.1:1.1] == [3:1:11]./10
187+
188+
@test [0.0:1.0:5.5] == [0:10:55]./10
189+
@test [0.0:-1.0:0.5] == []
190+
@test [0.0:1.0:0.5] == [0.0]
191+
192+
@test [prevfloat(0.1):0.1:0.3] == [prevfloat(0.1), 0.2, 0.3]
193+
@test [nextfloat(0.1):0.1:0.3] == [nextfloat(0.1), 0.2]
194+
@test [prevfloat(0.0):0.1:0.3] == [prevfloat(0.0), 0.1, 0.2]
195+
@test [nextfloat(0.0):0.1:0.3] == [nextfloat(0.0), 0.1, 0.2]
196+
@test [0.1:0.1:prevfloat(0.3)] == [0.1, 0.2]
197+
@test [0.1:0.1:nextfloat(0.3)] == [0.1, 0.2, nextfloat(0.3)]
198+
@test [0.0:0.1:prevfloat(0.3)] == [0.0, 0.1, 0.2]
199+
@test [0.0:0.1:nextfloat(0.3)] == [0.0, 0.1, 0.2, nextfloat(0.3)]
200+
@test [0.1:prevfloat(0.1):0.3] == [0.1, 0.2, 0.3]
201+
@test [0.1:nextfloat(0.1):0.3] == [0.1, 0.2]
202+
@test [0.0:prevfloat(0.1):0.3] == [0.0, prevfloat(0.1), prevfloat(0.2), 0.3]
203+
@test [0.0:nextfloat(0.1):0.3] == [0.0, nextfloat(0.1), nextfloat(0.2)]
204204

205205
for T = (Float32, Float64,),# BigFloat),
206206
a = -5:25, s = [-5:-1;1:25], d = 1:25, n = -1:15
@@ -210,3 +210,25 @@ for T = (Float32, Float64,),# BigFloat),
210210
stop = convert(T,(a+(n-1)*s))/den
211211
@test [start:step:stop] == T[a:s:a+(n-1)*s]./den
212212
end
213+
214+
# near-equal ranges
215+
@test 0.0:0.1:1.0 != Range(0.0,0.1,11)
216+
217+
# comparing and hashing ranges
218+
let
219+
Rs = {1:2, int32(1:3:17), int64(1:3:17), 1:0, 17:-3:0,
220+
0.0:0.1:1.0, Range(0.0,0.1,11),
221+
float32(0.0:0.1:1.0), float32(Range(0.0,0.1,11))}
222+
for r in Rs
223+
ar = collect(r)
224+
@test r != ar
225+
@test !isequal(r, ar)
226+
for s in Rs
227+
as = collect(s)
228+
229+
@test !isequal(r, s) || hash(r)==hash(s)
230+
231+
@test (r==s) == (ar==as)
232+
end
233+
end
234+
end

0 commit comments

Comments
 (0)