diff --git a/base/base.jl b/base/base.jl index 1309006009ab1..7b6d58fbb94c4 100644 --- a/base/base.jl +++ b/base/base.jl @@ -108,10 +108,9 @@ type Colon end const (:) = Colon() -hash(w::WeakRef) = hash(w.value) -isequal(w::WeakRef, v::WeakRef) = isequal(w.value, v.value) -isequal(w::WeakRef, v) = isequal(w.value, v) -isequal(w, v::WeakRef) = isequal(w, v.value) +==(w::WeakRef, v::WeakRef) = isequal(w.value, v.value) +==(w::WeakRef, v) = isequal(w.value, v) +==(w, v::WeakRef) = isequal(w, v.value) function finalizer(o::ANY, f::Union(Function,Ptr)) if isimmutable(o) diff --git a/base/bitarray.jl b/base/bitarray.jl index 78a604339a9a8..a83450b923abd 100644 --- a/base/bitarray.jl +++ b/base/bitarray.jl @@ -1720,9 +1720,3 @@ function cat(catdim::Integer, X::Union(BitArray, Integer)...) end # hvcat -> use fallbacks in abstractarray.jl - -isequal(A::BitArray, B::BitArray) = (A == B) - -# Hashing - -hash(B::BitArray) = hash((size(B), B.chunks)) diff --git a/base/bool.jl b/base/bool.jl index 1e84f74e5c05f..85638bf72e82f 100644 --- a/base/bool.jl +++ b/base/bool.jl @@ -23,7 +23,7 @@ typemax(::Type{Bool}) = true (|)(x::Bool, y::Bool) = box(Bool,or_int(unbox(Bool,x),unbox(Bool,y))) ($)(x::Bool, y::Bool) = (x!=y) -signbit(x::Bool) = 0 +signbit(x::Bool) = false sign(x::Bool) = x abs(x::Bool) = x abs2(x::Bool) = x diff --git a/base/comparison.jl b/base/comparison.jl new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/base/complex.jl b/base/complex.jl index 29c89042b653e..7203fc4788c43 100644 --- a/base/complex.jl +++ b/base/complex.jl @@ -99,8 +99,6 @@ end isequal(z::Complex, w::Complex) = isequal(real(z),real(w)) & isequal(imag(z),imag(w)) -hash(z::Complex) = bitmix(hash(real(z)),hash(imag(z))) - conj(z::Complex) = Complex(real(z),-imag(z)) abs(z::Complex) = hypot(real(z), imag(z)) abs2(z::Complex) = real(z)*real(z) + imag(z)*imag(z) diff --git a/base/constants.jl b/base/constants.jl index a0f1f522267d8..77310ba5c4e0d 100644 --- a/base/constants.jl +++ b/base/constants.jl @@ -13,6 +13,11 @@ convert(::Type{Float16}, x::MathConst) = float16(float32(x)) convert{T<:Real}(::Type{Complex{T}}, x::MathConst) = convert(Complex{T}, float64(x)) convert{T<:Integer}(::Type{Rational{T}}, x::MathConst) = convert(Rational{T}, float64(x)) +=={s}(::MathConst{s}, ::MathConst{s}) = true +==(::MathConst, ::MathConst) = false + +hash(x::MathConst, h::Uint) = hash(object_id(x), h) + -(x::MathConst) = -float64(x) for op in {:+, :-, :*, :/, :^} @eval $op(x::MathConst, y::MathConst) = $op(float64(x),float64(y)) diff --git a/base/dict.jl b/base/dict.jl index 4324217710f67..d106a52147169 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -94,14 +94,6 @@ filter(f::Function, d::Associative) = filter!(f,copy(d)) eltype{K,V}(a::Associative{K,V}) = (K,V) -function hash(d::Associative) - h::Uint = 0 - for (k,v) in d - h $= bitmix(hash(k),~hash(v)) - end - h -end - function isequal(l::Associative, r::Associative) if isa(l,ObjectIdDict) != isa(r,ObjectIdDict) return false @@ -201,96 +193,6 @@ function length(d::ObjectIdDict) n end -# hashing - -function int32hash(n::Uint32) - local a::Uint32 = n - a = (a + 0x7ed55d16) + a << 12 - a = (a $ 0xc761c23c) $ a >> 19 - a = (a + 0x165667b1) + a << 5 - a = (a + 0xd3a2646c) $ a << 9 - a = (a + 0xfd7046c5) + a << 3 - a = (a $ 0xb55a4f09) $ a >> 16 - return a -end - -function int64hash(n::Uint64) - local a::Uint64 = n - a = ~a + (a << 21) - a = a $ (a >> 24) - a = (a + (a << 3)) + (a << 8) - a = a $ (a >> 14) - a = (a + (a << 2)) + (a << 4) - a = a $ (a >> 28) - a = a + (a << 31) - return a -end - -function int64to32hash(n::Uint64) - local key::Uint64 = n - key = ~key + (key << 18) - key = key $ (key >> 31) - key = key * 21 - key = key $ (key >> 11) - key = key + (key << 6 ) - key = key $ (key >> 22) - return uint32(key) -end - -bitmix(a::Union(Int32,Uint32), b::Union(Int32,Uint32)) = int64to32hash((uint64(a)<<32)|uint64(b)) -bitmix(a::Union(Int64,Uint64), b::Union(Int64, Uint64)) = int64hash(uint64(a$((b<<32)|(b>>>32)))) - -if WORD_SIZE == 64 - hash64(x::Float64) = int64hash(reinterpret(Uint64,x)) - hash64(x::Union(Int64,Uint64)) = int64hash(reinterpret(Uint64,x)) -else - hash64(x::Float64) = int64to32hash(reinterpret(Uint64,x)) - hash64(x::Union(Int64,Uint64)) = int64to32hash(reinterpret(Uint64,x)) -end - -hash(x::Union(Bool,Char,Int8,Uint8,Int16,Uint16,Int32,Uint32,Int64,Uint64)) = - hash64(uint64(x)) - -function hash(x::Integer) - h::Uint = hash(uint64(x&0xffffffffffffffff)) - if typemin(Int64) <= x <= typemax(Uint64) - return h - end - x >>>= 64 - while x != 0 && x != -1 - h = bitmix(h, hash(uint64(x&0xffffffffffffffff))) - x >>>= 64 - end - return h -end - -hash(x::Float32) = hash(reinterpret(Uint32, ifelse(isnan(x), NaN32, x))) -hash(x::Float64) = hash(reinterpret(Uint64, ifelse(isnan(x), NaN, x))) - -function hash(t::Tuple) - h::Uint = 0 - for i=1:length(t) - h = bitmix(h,int(hash(t[i]))+42) - end - return h -end - -function hash(a::AbstractArray) - h::Uint = hash(size(a))+1 - for i=1:length(a) - h = bitmix(h,int(hash(a[i]))) - end - return h -end - -# make sure Array{Bool} and BitArray can be equivalent -hash(a::AbstractArray{Bool}) = hash(bitpack(a)) - -hash(x::ANY) = object_id(x) - -hash(x::Expr) = bitmix(hash(x.head),hash(x.args)+43) - - # dict type Dict{K,V} <: Associative{K,V} @@ -538,6 +440,7 @@ function setindex!{K,V}(h::Dict{K,V}, v0, key0) index = ht_keyindex2(h, key) if index > 0 + h.keys[index] = key h.vals[index] = v else _setindex!(h, v, key, -index) diff --git a/base/exports.jl b/base/exports.jl index 7d46dfe68d65d..a0c47747b95cb 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -290,7 +290,6 @@ export atanh, big, binomial, - bitmix, bool, bswap, cbrt, diff --git a/base/expr.jl b/base/expr.jl index b9f09abd54425..b4d759fa9d3fc 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -39,8 +39,8 @@ astcopy(x::Union(SymbolNode,GetfieldNode,Expr)) = copy(x) astcopy(x::Array{Any,1}) = map(astcopy, x) astcopy(x) = x -isequal(x::Expr, y::Expr) = (is(x.head,y.head) && isequal(x.args,y.args)) -isequal(x::QuoteNode, y::QuoteNode) = isequal(x.value, y.value) +==(x::Expr, y::Expr) = x.head === y.head && x.args == y.args +==(x::QuoteNode, y::QuoteNode) = x.value == y.value function show(io::IO, tv::TypeVar) if !is(tv.lb, None) diff --git a/base/float.jl b/base/float.jl index d805574a39f3d..44b66df09f3cf 100644 --- a/base/float.jl +++ b/base/float.jl @@ -150,19 +150,11 @@ mod{T<:FloatingPoint}(x::T, y::T) = rem(y+rem(x,y),y) <=(x::Float32, y::Float32) = le_float(unbox(Float32,x),unbox(Float32,y)) <=(x::Float64, y::Float64) = le_float(unbox(Float64,x),unbox(Float64,y)) -isequal{T<:FloatingPoint}(x::T, y::T) = - ((x==y) & (signbit(x)==signbit(y))) | (isnan(x)&isnan(y)) - isequal(x::Float32, y::Float32) = fpiseq(unbox(Float32,x),unbox(Float32,y)) isequal(x::Float64, y::Float64) = fpiseq(unbox(Float64,x),unbox(Float64,y)) isless (x::Float32, y::Float32) = fpislt(unbox(Float32,x),unbox(Float32,y)) isless (x::Float64, y::Float64) = fpislt(unbox(Float64,x),unbox(Float64,y)) -isless(a::FloatingPoint, b::FloatingPoint) = - (asignbit(b)))) -isless(a::Real, b::FloatingPoint) = (ay, 1, 0)) @@ -220,18 +212,15 @@ end abs(x::Float64) = box(Float64,abs_float(unbox(Float64,x))) abs(x::Float32) = box(Float32,abs_float(unbox(Float32,x))) -isnan(x::FloatingPoint) = (x != x) -isnan(x::Real) = isnan(float(x)) -isnan(x::Integer) = false +isnan(x::FloatingPoint) = x != x +isnan(x::Real) = false -isinf(x::FloatingPoint) = (abs(x) == Inf) -isinf(x::Real) = isinf(float(x)) -isinf(x::Integer) = false - -isfinite(x::FloatingPoint) = (x-x == 0) -isfinite(x::Real) = isfinite(float(x)) +isfinite(x::FloatingPoint) = x - x == 0 +isfinite(x::Real) = decompose(x)[3] != 0 isfinite(x::Integer) = true +isinf(x::Real) = !isnan(x) & !isfinite(x) + ## floating point traits ## const Inf16 = box(Float16,unbox(Uint16,0x7c00)) diff --git a/base/float16.jl b/base/float16.jl index 303529d2afe7d..158ed6dbf3e4c 100644 --- a/base/float16.jl +++ b/base/float16.jl @@ -139,5 +139,3 @@ hypot(a::Float16, b::Float16) = float16(hypot(float32(a), float32(b))) ldexp(a::Float16, b::Integer) = float16(ldexp(float32(a), b)) exponent(x::Float16) = exponent(float32(x)) ^(x::Float16, y::Integer) = x^float16(y) - -hash(x::Float16) = hash(reinterpret(Uint16, isnan(x) ? NaN16 : x)) diff --git a/base/gmp.jl b/base/gmp.jl index 5f8733c63abd0..af2ea296a06c7 100644 --- a/base/gmp.jl +++ b/base/gmp.jl @@ -407,7 +407,6 @@ end binomial(n::BigInt, k::Integer) = k < 0 ? throw(DomainError()) : binomial(n, uint(k)) ==(x::BigInt, y::BigInt) = cmp(x,y) == 0 -isequal(x::BigInt, y::BigInt) = cmp(x,y) == 0 <=(x::BigInt, y::BigInt) = cmp(x,y) <= 0 >=(x::BigInt, y::BigInt) = cmp(x,y) >= 0 <(x::BigInt, y::BigInt) = cmp(x,y) < 0 diff --git a/base/hashing.jl b/base/hashing.jl new file mode 100644 index 0000000000000..44e7971b533bb --- /dev/null +++ b/base/hashing.jl @@ -0,0 +1,81 @@ +## hashing a single value ## + +hash(x::Any) = hash(x, zero(Uint)) +hash(w::WeakRef, h::Uint) = hash(w.value, h) + +## core data hashing functions ## + +function hash_64_64(n::Uint64) + local a::Uint64 = n + a = ~a + a << 21 + a = a $ a >> 24 + a = a + a << 3 + a << 8 + a = a $ a >> 14 + a = a + a << 2 + a << 4 + a = a $ a >> 28 + a = a + a << 31 + return a +end + +function hash_64_32(n::Uint64) + local a::Uint64 = n + a = ~a + a << 18 + a = a $ a >> 31 + a = a * 21 + a = a $ a >> 11 + a = a + a << 6 + a = a $ a >> 22 + return uint32(a) +end + +function hash_32_32(n::Uint32) + local a::Uint32 = n + a = a + 0x7ed55d16 + a << 12 + a = a $ 0xc761c23c $ a >> 19 + a = a + 0x165667b1 + a << 5 + a = a + 0xd3a2646c $ a << 9 + a = a + 0xfd7046c5 + a << 3 + a = a $ 0xb55a4f09 $ a >> 16 + return a +end + +if Uint == Uint64 + hash_uint64(x::Uint64) = hash_64_64(x) + hash_uint(x::Uint) = hash_64_64(x) +else + hash_uint64(x::Uint64) = hash_64_32(x) + hash_uint(x::Uint) = hash_32_32(x) +end + +## hashing small, built-in numeric types ## + +hx(a::Uint64, b::Float64, h::Uint) = hash_uint64((3a + reinterpret(Uint64,b)) - h) +const hx_NaN = hx(uint64(0), NaN, uint(0 )) + +hash(x::Uint64, h::Uint) = hx(x, float64(x), h) +hash(x::Int64, h::Uint) = hx(reinterpret(Uint64,x), float64(x), h) +hash(x::Float64, h::Uint) = isnan(x) ? (hx_NaN $ h) : hx(box(Uint64,fptosi(unbox(Float64,x))), x, h) + +hash(x::Union(Int8,Uint8,Int16,Uint16,Int32,Uint32), h::Uint) = hash(int64(x), h) +hash(x::Float32, h::Uint) = hash(float64(x), h) + +## hashing complex numbers ## + +const h_imag = uint(0x32a7a07f3e7cd1f9) +const hash_0_imag = hash(0, h_imag) + +function hash(z::Complex, h::Uint) + # TODO: with default argument specialization, this would be better: + # hash(real(z), h $ hash(imag(z), h $ h_imag) $ hash(0, h $ h_imag)) + hash(real(z), h $ hash(imag(z), h_imag) $ hash_0_imag) +end + +## special hashing for booleans and characters ## + +hash(x::Bool, h::Uint) = hash(int(x), h + uint(0x4cd135a1755139a5)) +hash(x::Char, h::Uint) = hash(int(x), h + uint(0x10f989ff0f886f11)) + +## symbol & expression hashing ## + +hash(x::Symbol, h::Uint) = hash(object_id(x), h) +hash(x::Expr, h::Uint) = hash(x.args, hash(x.head, h + uint(0x83c7900696d26dc6))) diff --git a/base/hashing2.jl b/base/hashing2.jl new file mode 100644 index 0000000000000..fececd3784446 --- /dev/null +++ b/base/hashing2.jl @@ -0,0 +1,193 @@ +## efficient value-based hashing of integers ## + +function hash_integer(n::Integer, h::Uint) + h = hash_uint(uint(n & typemax(Uint)) $ h) $ h + n = ifelse(n < 0, oftype(n,-n), n) + n >>>= sizeof(Uint) << 3 + while n != 0 + h = hash_uint(uint(n & typemax(Uint)) $ h) $ h + n >>>= sizeof(Uint) << 3 + end + return h +end + +function hash_integer(n::BigInt, h::Uint) + s = n.size + s == 0 && return hash_integer(0, h) + p = convert(Ptr{Uint}, n.d) + b = unsafe_load(p) + h = hash_uint(ifelse(s < 0, -b, b) $ h) $ h + for k = 2:abs(s) + h = hash_uint(unsafe_load(p, k) $ h) $ h + end + return h +end + +## generic hashing for rational values ## + +function hash(x::Real, h::Uint) + # decompose x as num*2^pow/den + num, pow, den = decompose(x) + + # handle special values + num == 0 && den == 0 && return hash(NaN, h) + num == 0 && return hash(ifelse(den > 0, 0.0, -0.0), h) + den == 0 && return hash(ifelse(num > 0, Inf, -Inf), h) + + # normalize decomposition + if den < 0 + num = -num + den = -den + end + z = trailing_zeros(num) + if z != 0 + num >>= z + pow += z + end + z = trailing_zeros(den) + if z != 0 + den >>= z + pow -= z + end + + # handle values representable as Int64, Uint64, Float64 + if den == 1 + left = ndigits0z(num,2) + pow + right = trailing_zeros(num) + pow + if -1074 <= right + if 0 <= right && left <= 64 + left <= 63 && return hash(int64(num) << int(pow), h) + signbit(num) == signbit(den) && return hash(uint64(num) << int(pow), h) + end # typemin(Int64) handled by Float64 case + left <= 1024 && left - right <= 53 && return hash(float64(num) * 2.0^pow, h) + end + end + + # handle generic rational values + h = hash_integer(den, h) + h = hash_integer(pow, h) + h = hash_integer(num, h) + return h +end + +#= +`decompose(x)`: non-canonical decomposition of rational values as `num*2^pow/den`. + +The decompose function is the point where rational-valued numeric types that support +hashing hook into the hashing protocol. `decompose(x)` should return three integer +values `num, pow, den`, such that the value of `x` is mathematically equal to + + num*2^pow/den + +The decomposition need not be canonical in the sense that it just needs to be *some* +way to express `x` in this form, not any particular way – with the restriction that +`num` and `den` may not share any odd common factors. They may, however, have powers +of two in common – the generic hashing code will normalize those as necessary. + +Special values: + + - `x` is zero: `num` should be zero and `den` should have the same sign as `x` + - `x` is infinite: `den` should be zero and `num` should have the same sign as `x` + - `x` is not a number: `num` and `den` should both be zero +=# + +decompose(x::Integer) = x, 0, 1 +decompose(x::Rational) = num(x), 0, den(x) + +function decompose(x::Float32) + isnan(x) && return 0, 0, 0 + isinf(x) && return ifelse(x < 0, -1, 1), 0, 0 + n = reinterpret(Int32, x) + s = int32(n & 0x007fffff) + e = int32(n & 0x7f800000 >> 23) + s |= int32(e != 0) << 23 + d = ifelse(signbit(n) == 1, -1, 1) + int(s), int(e - 150 + (e == 0)), d +end + +function decompose(x::Float64) + isnan(x) && return 0, 0, 0 + isinf(x) && return ifelse(x < 0, -1, 1), 0, 0 + n = reinterpret(Int64, x) + s = int64(n & 0x000fffffffffffff) + e = int64(n & 0x7ff0000000000000 >> 52) + s |= int64(e != 0) << 52 + d = ifelse(signbit(n) == 1, -1, 1) + int(s), int(e - 1075 + (e == 0)), d +end + +function decompose(x::BigFloat) + isnan(x) && return big(0), 0, 0 + isinf(x) && return big(x.sign), 0, 0 + x == 0 && return big(0), 0, int(x.sign) + s = BigInt() + ccall((:__gmpz_realloc2, :libgmp), Void, (Ptr{BigInt}, Culong), &s, x.prec) + s.size = -fld(-x.prec,(sizeof(Culong)<<3)) + ccall(:memcpy, Ptr{Void}, (Ptr{Void}, Ptr{Void}, Csize_t), s.d, x.d, s.size*sizeof(Culong)) + s, int(x.exp - x.prec), int(x.sign) +end + +## streamlined hashing for smallish rational types ## + +function hash{T<:Integer64}(x::Rational{T}, h::Uint) + num, den = Base.num(x), Base.den(x) + den == 1 && return hash(num, h) + den == 0 && return hash(ifelse(num > 0, Inf, -Inf), h) + if isodd(den) + pow = trailing_zeros(num) + num >>= pow + else + pow = trailing_zeros(den) + den >>= pow + pow = -pow + if den == 1 && abs(num) < 9007199254740992 + return hash(float64(num) * 2.0^pow) + end + end + h = hash_integer(den, h) + h = hash_integer(pow, h) + h = hash_integer(num, h) + return h +end + +## hashing Float16s ## + +hash(x::Float16, h::Uint) = hash(float64(x), h) + +## hashing strings ## + +const memhash = Uint == Uint64 ? :memhash_seed : :memhash32_seed + +function hash{T<:ByteString}(s::Union(T,SubString{T}), h::Uint) + h += uint(0x71e729fd56419c81) + ccall(memhash, Uint, (Ptr{Uint8}, Csize_t, Uint32), pointer(s), sizeof(s), h) + h +end +hash(s::String, h::Uint) = hash(bytestring(s), h) + +## hashing collections ## + +function hash(v::Union(Tuple,AbstractArray,Associative), h::Uint) + h += object_id(eltype(v)) + for x = v + h = hash(x, h) + end + return h +end + +hash(s::Set, h::Uint) = hash(sort(s.dict.keys[s.dict.slots .!= 0]), h) + +hash(r::Range{Bool}, h::Uint) = invoke(hash, (Range, Uint), r, h) +hash(B::BitArray, h::Uint) = hash((size(B),B.chunks), h) +hash(a::AbstractArray{Bool}, h::Uint) = hash(bitpack(a), h) + +# hashing ranges by component at worst leads to collisions for very similar ranges +function hash{T<:Range}(r::T, h::Uint) + h += uint(0x80707b6821b70087) + h = hash(first(r), h) + h = hash(step(r), h) + h = hash(last(r), h) +end + +## hashing general objects ## + +hash(x::ANY, h::Uint) = hash(object_id(x), h) diff --git a/base/int.jl b/base/int.jl index 3ddecb3fb8aeb..7d510d4a6d618 100644 --- a/base/int.jl +++ b/base/int.jl @@ -53,12 +53,8 @@ inv(x::Integer) = float(one(x))/float(x) isodd(n::Integer) = bool(rem(n,2)) iseven(n::Integer) = !isodd(n) -signbit(x::Unsigned) = 0 -signbit(x::Int8) = int(x>>>7) -signbit(x::Int16) = int(x>>>15) -signbit(x::Int32) = int(x>>>31) -signbit(x::Int64) = int(x>>>63) -signbit(x::Int128) = int(x>>>127) +signbit(x::Integer) = x < 0 +signbit(x::Unsigned) = false flipsign(x::Int, y::Int) = box(Int,flipsign_int(unbox(Int,x),unbox(Int,y))) flipsign(x::Int64, y::Int64) = box(Int64,flipsign_int(unbox(Int64,x),unbox(Int64,y))) @@ -96,6 +92,8 @@ mod(x::Unsigned, y::Signed) = rem(y+signed(rem(x,y)),y) # while there is a substantial performance penalty to 64-bit promotion. typealias Signed64 Union(Int8,Int16,Int32,Int64) typealias Unsigned64 Union(Uint8,Uint16,Uint32,Uint64) +typealias Integer64 Union(Signed64,Unsigned64) + div{T<:Signed64} (x::T, y::T) = box(T,sdiv_int(unbox(T,x),unbox(T,y))) div{T<:Unsigned64}(x::T, y::T) = box(T,udiv_int(unbox(T,x),unbox(T,y))) rem{T<:Signed64} (x::T, y::T) = box(T,srem_int(unbox(T,x),unbox(T,y))) diff --git a/base/intset.jl b/base/intset.jl index 383be0f6ec88e..d383925026785 100644 --- a/base/intset.jl +++ b/base/intset.jl @@ -258,7 +258,7 @@ function symdiff!(s::IntSet, s2::IntSet) s end -function isequal(s1::IntSet, s2::IntSet) +function ==(s1::IntSet, s2::IntSet) if s1.fill1s != s2.fill1s return false end diff --git a/base/mpfr.jl b/base/mpfr.jl index 744c39e0d124e..bd1f39532ba3f 100644 --- a/base/mpfr.jl +++ b/base/mpfr.jl @@ -16,7 +16,7 @@ import gamma, lgamma, digamma, erf, erfc, zeta, log1p, airyai, iceil, ifloor, itrunc, eps, signbit, sin, cos, tan, sec, csc, cot, acos, asin, atan, cosh, sinh, tanh, sech, csch, coth, acosh, asinh, atanh, atan2, - serialize, deserialize, inf, nan, hash, cbrt, typemax, typemin, + serialize, deserialize, inf, nan, cbrt, typemax, typemin, realmin, realmax, get_rounding, set_rounding, maxintfloat, widen import Base.GMP: ClongMax, CulongMax @@ -580,8 +580,7 @@ end <(x::BigFloat, y::BigFloat) = ccall((:mpfr_less_p, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}), &x, &y) != 0 >(x::BigFloat, y::BigFloat) = ccall((:mpfr_greater_p, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}), &x, &y) != 0 -signbit(x::BigFloat) = - int(ccall((:mpfr_signbit, :libmpfr), Int32, (Ptr{BigFloat},), &x)!=0) +signbit(x::BigFloat) = ccall((:mpfr_signbit, :libmpfr), Int32, (Ptr{BigFloat},), &x) != 0 function precision(x::BigFloat) return ccall((:mpfr_get_prec, :libmpfr), Clong, (Ptr{BigFloat},), &x) @@ -714,24 +713,4 @@ print(io::IO, b::BigFloat) = print(io, string(b)) show(io::IO, b::BigFloat) = print(io, string(b), " with $(precision(b)) bits of precision") showcompact(io::IO, b::BigFloat) = print(io, string(b)) -function hash(x::BigFloat) - if isnan(x) - return hash(NaN) - end - if isinf(x) - return hash(float64(x)) - end - n = ceil(precision(x)/53) - e = exponent(x) - h::Uint = signbit(x) - h = h<<30 + e - x = ldexp(x, -e) - for i=1:n - f64 = float64(x) - h = bitmix(h, hash(f64)$11111) - x -= f64 - end - h -end - end #module diff --git a/base/multi.jl b/base/multi.jl index 5f9c2770fc6c8..b9e70f292f9b8 100644 --- a/base/multi.jl +++ b/base/multi.jl @@ -448,7 +448,7 @@ type RemoteRef next_id() = (id=(myid(),REQ_ID); REQ_ID+=1; id) end -hash(r::RemoteRef) = hash(r.whence)+3*hash(r.id) +hash(r::RemoteRef, h::Uint) = hash(r.whence, hash(r.id, h)) isequal(r::RemoteRef, s::RemoteRef) = (r.whence==s.whence && r.id==s.id) rr2id(r::RemoteRef) = (r.whence, r.id) diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 0b78db5b085d0..cde16603f0e9b 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -480,6 +480,8 @@ end ## unique across dim +# TODO: this doesn't fit into the new hashing scheme in any obvious way + immutable Prehashed hash::Uint end @@ -492,7 +494,7 @@ hash(x::Prehashed) = x.hash # Compute hash for each row k = 0 @nloops N i A d->(if d == dim; k = i_d; end) begin - @inbounds hashes[k] = bitmix(hashes[k], hash((@nref N A i))) + @inbounds hashes[k] = hash(hashes[k], hash((@nref N A i))) end # Collect index of first row for each hash diff --git a/base/number.jl b/base/number.jl index f54230d1d272e..6dcfc24c7fc04 100644 --- a/base/number.jl +++ b/base/number.jl @@ -18,7 +18,7 @@ first(x::Number) = x last(x::Number) = x divrem(x,y) = (div(x,y),rem(x,y)) -signbit(x::Real) = int(x < 0) +signbit(x::Real) = x < 0 sign(x::Real) = ifelse(x < 0, oftype(x,-1), ifelse(x > 0, one(x), x)) abs(x::Real) = ifelse(x < 0, -x, x) abs2(x::Real) = x*x diff --git a/base/operators.jl b/base/operators.jl index 2471220f322d1..f73fcaa423ef2 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -4,27 +4,39 @@ const (<:) = issubtype super(T::DataType) = T.super +## generic comparison ## + +==(x,y) = x === y + +isequal(x, y) = x == y +isequal(x::FloatingPoint, y::FloatingPoint) = (isnan(x) & isnan(y)) | (signbit(x) == signbit(y)) & (x == y) +isequal(x::Real, y::FloatingPoint) = (isnan(x) & isnan(y)) | (signbit(x) == signbit(y)) & (x == y) +isequal(x::FloatingPoint, y::Real ) = (isnan(x) & isnan(y)) | (signbit(x) == signbit(y)) & (x == y) + +isless(x::FloatingPoint, y::FloatingPoint) = (!isnan(x) & isnan(y)) | (signbit(x) & !signbit(y)) | (x < y) +isless(x::Real, y::FloatingPoint) = (!isnan(x) & isnan(y)) | (signbit(x) & !signbit(y)) | (x < y) +isless(x::FloatingPoint, y::Real ) = (!isnan(x) & isnan(y)) | (signbit(x) & !signbit(y)) | (x < y) + # avoid ambiguity with isequal(::Tuple, ::Tuple) -isequal(T::(Type...), S::(Type...)) = typeseq(T, S) -isequal(T::Type, S::Type) = typeseq(T, S) +==(T::(Type...), S::(Type...)) = typeseq(T, S) +==(T::Type, S::Type) = typeseq(T, S) -## comparison ## +## comparison fallbacks ## -isequal(x,y) = is(x,y) -==(x,y) = isequal(x,y) !=(x,y) = !(x==y) const ≠ = != const ≡ = is !==(x,y) = !is(x,y) const ≢ = !== -< (x,y) = isless(x,y) -> (x,y) = y < x + +<(x,y) = isless(x,y) +>(x,y) = y < x <=(x,y) = !(y < x) const ≤ = <= >=(x,y) = (y <= x) const ≥ = >= -.> (x,y) = y.=(x,y) = y.<=x +.>(x,y) = y .< x +.>=(x,y) = y .<= x const .≥ = .>= # this definition allows Number types to implement < instead of isless, @@ -85,20 +97,20 @@ end .+(x,y) = x+y .-(x,y) = x-y -.==(x::Number,y::Number) = x==y -.!=(x::Number,y::Number) = x!=y -.< (x::Real,y::Real) = x> and >>> takes Int32 as second arg -<<(x,y::Integer) = x << convert(Int32,y) <<(x,y::Int32) = no_op_err("<<", typeof(x)) ->>(x,y::Integer) = x >> convert(Int32,y) >>(x,y::Int32) = no_op_err(">>", typeof(x)) ->>>(x,y::Integer) = x >>> convert(Int32,y) >>>(x,y::Int32) = no_op_err(">>>", typeof(x)) +<<(x,y::Integer) = x << convert(Int32,y) +>>(x,y::Integer) = x >> convert(Int32,y) +>>>(x,y::Integer) = x >>> convert(Int32,y) # fallback div and fld implementations # NOTE: C89 fmod() and x87 FPREM implicitly provide truncating float division, diff --git a/base/pkg/reqs.jl b/base/pkg/reqs.jl index bf7fe90202e15..b8b58bfecded9 100644 --- a/base/pkg/reqs.jl +++ b/base/pkg/reqs.jl @@ -46,7 +46,7 @@ immutable Requirement <: Line end # TODO: shouldn't be neccessary #4648 -Base.isequal(a::Line, b::Line) = (a.content == b.content) +==(a::Line, b::Line) = a.content == b.content # general machinery for parsing REQUIRE files diff --git a/base/pkg/resolve/fieldvalue.jl b/base/pkg/resolve/fieldvalue.jl index d450121bfee99..5884b666fc371 100644 --- a/base/pkg/resolve/fieldvalue.jl +++ b/base/pkg/resolve/fieldvalue.jl @@ -55,7 +55,7 @@ function Base.isless(a::FieldValue, b::FieldValue) return false end -Base.isequal(a::FieldValue, b::FieldValue) = +==(a::FieldValue, b::FieldValue) = a.l0 == b.l0 && a.l1 == b.l1 && a.l2 == b.l2 && a.l3 == b.l3 && a.l4 == b.l4 Base.abs(a::FieldValue) = FieldValue(abs(a.l0), abs(a.l1), abs(a.l2), abs(a.l3), abs(a.l4)) diff --git a/base/pkg/resolve/versionweight.jl b/base/pkg/resolve/versionweight.jl index 05c9803fbb473..f89360e68918b 100644 --- a/base/pkg/resolve/versionweight.jl +++ b/base/pkg/resolve/versionweight.jl @@ -60,8 +60,8 @@ function Base.cmp{T}(a::HierarchicalValue{T}, b::HierarchicalValue{T}) end return cmp(a.rest, b.rest) end -Base.isless{T}(a::HierarchicalValue{T}, b::HierarchicalValue{T}) = (cmp(a, b) == -1) -Base.isequal{T}(a::HierarchicalValue{T}, b::HierarchicalValue{T}) = (a.v == b.v) && (a.rest == b.rest) +Base.isless{T}(a::HierarchicalValue{T}, b::HierarchicalValue{T}) = cmp(a,b) < 0 +=={T}(a::HierarchicalValue{T}, b::HierarchicalValue{T}) = a.v == b.v && a.rest == b.rest Base.abs{T}(a::HierarchicalValue{T}) = HierarchicalValue(T[abs(x) for x in a.v], abs(a.rest)) @@ -88,8 +88,8 @@ function Base.cmp(a::VWPreBuildItem, b::VWPreBuildItem) c = cmp(a.s, b.s); c != 0 && return c return cmp(a.i, b.i) end -Base.isless(a::VWPreBuildItem, b::VWPreBuildItem) = (cmp(a,b) == -1) -Base.isequal(a::VWPreBuildItem, b::VWPreBuildItem) = (cmp(a,b) == 0) +Base.isless(a::VWPreBuildItem, b::VWPreBuildItem) = cmp(a,b) < 0 +==(a::VWPreBuildItem, b::VWPreBuildItem) = cmp(a,b) == 0 Base.abs(a::VWPreBuildItem) = VWPreBuildItem(abs(a.nonempty), abs(a.s), abs(a.i)) @@ -125,8 +125,8 @@ function Base.cmp(a::VWPreBuild, b::VWPreBuild) c = cmp(a.nonempty, b.nonempty); c != 0 && return c return cmp(a.w, b.w) end -Base.isless(a::VWPreBuild, b::VWPreBuild) = (cmp(a,b) == -1) -Base.isequal(a::VWPreBuild, b::VWPreBuild) = (cmp(a,b) == 0) +Base.isless(a::VWPreBuild, b::VWPreBuild) = cmp(a,b) < 0 +==(a::VWPreBuild, b::VWPreBuild) = cmp(a,b) == 0 Base.abs(a::VWPreBuild) = VWPreBuild(abs(a.nonempty), abs(a.w)) @@ -179,8 +179,8 @@ function Base.cmp(a::VersionWeight, b::VersionWeight) c = cmp(a.build, b.build); c != 0 && return c return cmp(a.uninstall, b.uninstall) end -Base.isless(a::VersionWeight, b::VersionWeight) = (cmp(a, b) == -1) -Base.isequal(a::VersionWeight, b::VersionWeight) = (cmp(a, b) == 0) +Base.isless(a::VersionWeight, b::VersionWeight) = cmp(a,b) < 0 +==(a::VersionWeight, b::VersionWeight) = cmp(a,b) == 0 Base.abs(a::VersionWeight) = VersionWeight(abs(a.major), abs(a.minor), abs(a.patch), diff --git a/base/pkg/types.jl b/base/pkg/types.jl index c2d75ffe13a47..fb8b63513527e 100644 --- a/base/pkg/types.jl +++ b/base/pkg/types.jl @@ -1,7 +1,7 @@ module Types export VersionInterval, VersionSet, Requires, Available, Fixed, merge_requires!, satisfies -import Base: show, isempty, in, intersect, isequal, hash, deepcopy_internal +import Base: show, isempty, in, intersect, hash, deepcopy_internal immutable VersionInterval lower::VersionNumber @@ -14,7 +14,7 @@ show(io::IO, i::VersionInterval) = print(io, "[$(i.lower),$(i.upper))") isempty(i::VersionInterval) = i.upper <= i.lower in(v::VersionNumber, i::VersionInterval) = i.lower <= v < i.upper intersect(a::VersionInterval, b::VersionInterval) = VersionInterval(max(a.lower,b.lower), min(a.upper,b.upper)) -isequal(a::VersionInterval, b::VersionInterval) = (a.lower == b.lower) & (a.upper == b.upper) +==(a::VersionInterval, b::VersionInterval) = a.lower == b.lower && a.upper == b.upper immutable VersionSet intervals::Vector{VersionInterval} @@ -43,8 +43,8 @@ function intersect(A::VersionSet, B::VersionSet) sort!(ivals, by=i->i.lower) VersionSet(ivals) end -isequal(A::VersionSet, B::VersionSet) = (A.intervals == B.intervals) -hash(s::VersionSet) = hash(s.intervals) +==(A::VersionSet, B::VersionSet) = A.intervals == B.intervals +hash(s::VersionSet, h::Uint) = hash(s.intervals, h + uint(0x2fd2ca6efa023f44)) deepcopy_internal(vs::VersionSet, ::ObjectIdDict) = VersionSet(copy(vs.intervals)) typealias Requires Dict{ByteString,VersionSet} @@ -64,7 +64,7 @@ immutable Available requires::Requires end -isequal(a::Available, b::Available) = (a.sha1 == b.sha1 && a.requires == b.requires) +==(a::Available, b::Available) = a.sha1 == b.sha1 && a.requires == b.requires show(io::IO, a::Available) = isempty(a.requires) ? print(io, "Available(", repr(a.sha1), ")") : @@ -76,7 +76,7 @@ immutable Fixed end Fixed(v::VersionNumber) = Fixed(v,Requires()) -isequal(a::Fixed, b::Fixed) = (a.version == b.version && a.requires == b.requires) +==(a::Fixed, b::Fixed) = a.version == b.version && a.requires == b.requires show(io::IO, f::Fixed) = isempty(f.requires) ? print(io, "Fixed(", repr(f.version), ")") : diff --git a/base/precompile.jl b/base/precompile.jl index fcdfad42cea5f..44200d159789b 100644 --- a/base/precompile.jl +++ b/base/precompile.jl @@ -52,9 +52,6 @@ precompile(bool, (RemoteRef,)) precompile(wait, (RemoteRef,)) precompile(hash, (RemoteRef,)) precompile(take, (RemoteRef,)) -precompile(bitmix, (Int, Int)) -precompile(bitmix, (Uint, Int)) -precompile(bitmix, (Uint64, Int64)) precompile(hash, (Int,)) precompile(isequal, (Symbol, Symbol)) precompile(isequal, (Bool, Bool)) diff --git a/base/profile.jl b/base/profile.jl index 8be1b7c197101..c6091f9d33b42 100644 --- a/base/profile.jl +++ b/base/profile.jl @@ -69,7 +69,12 @@ const UNKNOWN = LineInfo("?", "?", -1, true) isequal(a::LineInfo, b::LineInfo) = a.line == b.line && a.fromC == b.fromC && a.func == b.func && a.file == b.file -hash(li::LineInfo) = bitmix(hash(li.func), bitmix(hash(li.file), bitmix(hash(li.line), hash(li.fromC)))) +function hash(li::LineInfo, h::Uint) + h += uint(0xf4fbda67fe20ce88) + h = hash(li.line, h) + h = hash(li.file, h) + h = hash(li.func, h) +end # C wrappers start_timer() = ccall(:jl_profile_start_timer, Cint, ()) diff --git a/base/random.jl b/base/random.jl index 9f3e1ba1b1b87..c3d6aa95ef5ea 100644 --- a/base/random.jl +++ b/base/random.jl @@ -39,11 +39,9 @@ function __init__() catch println(STDERR, "Entropy pool not available to seed RNG; using ad-hoc entropy sources.") seed = reinterpret(Uint64, time()) - seed = bitmix(seed, uint64(getpid())) + seed = hash(seed, uint64(getpid())) try - seed = bitmix(seed, parseint(Uint64, readall(`ifconfig` |> `sha1sum`)[1:40], 16)) - catch - # ignore + seed = hash(seed, parseint(Uint64, readall(`ifconfig` |> `sha1sum`)[1:40], 16)) end srand(seed) end diff --git a/base/range.jl b/base/range.jl index bebf1d69921a9..43b1b75c91c95 100644 --- a/base/range.jl +++ b/base/range.jl @@ -273,15 +273,7 @@ function show(io::IO, r::Range) end show(io::IO, r::UnitRange) = print(io, repr(first(r)), ':', repr(last(r))) -isequal{T<:Range}(r::T, s::T) = - (first(r)==first(s)) & (step(r)==step(s)) & (last(r)==last(s)) - -isequal(r::Range, s::Range) = false - -=={T<:Range}(r::T, s::T) = isequal(r, s) - -=={T<:Integer, S<:Integer}(r::Range{T}, s::Range{S}) = - (first(r)==first(s)) & (step(r)==step(s)) & (last(r)==last(s)) +=={T<:Range}(r::T, s::T) = (first(r) == first(s)) & (step(r) == step(s)) & (last(r) == last(s)) function ==(r::Range, s::Range) lr = length(r) @@ -299,12 +291,6 @@ function ==(r::Range, s::Range) return true end -# hashing ranges by component at worst leads to collisions for very similar ranges -hash(r::Range) = - bitmix(hash(first(r)), bitmix(hash(step(r)), bitmix(hash(last(r)), uint(0xaaeeaaee)))) - -# TODO: isless? - intersect{T1<:Integer, T2<:Integer}(r::UnitRange{T1}, s::UnitRange{T2}) = max(r.start,s.start):min(last(r),last(s)) intersect{T<:Integer}(i::Integer, r::UnitRange{T}) = diff --git a/base/rational.jl b/base/rational.jl index 2c22105a99465..82a96aa9cbcc7 100644 --- a/base/rational.jl +++ b/base/rational.jl @@ -101,17 +101,11 @@ signbit(x::Rational) = signbit(x.num) copysign(x::Rational, y::Real) = copysign(x.num,y) // x.den copysign(x::Rational, y::Rational) = copysign(x.num,y.num) // x.den -isnan(x::Rational) = false -isinf(x::Rational) = x.den == 0 -isfinite(x::Rational) = x.den != 0 - typemin{T<:Integer}(::Type{Rational{T}}) = -one(T)//zero(T) typemax{T<:Integer}(::Type{Rational{T}}) = one(T)//zero(T) isinteger(x::Rational) = x.den == 1 -hash(x::Rational) = bitmix(hash(x.num), ~hash(x.den)) - -(x::Rational) = (-x.num) // x.den for op in (:+, :-, :rem, :mod) @eval begin diff --git a/base/set.jl b/base/set.jl index 297a31d007453..b4502f8d6fe80 100644 --- a/base/set.jl +++ b/base/set.jl @@ -75,8 +75,8 @@ function setdiff(a::Set, b::Set) d end -isequal(l::Set, r::Set) = (length(l) == length(r)) && (l <= r) -<(l::Set, r::Set) = (length(l) < length(r)) && (l <= r) +==(l::Set, r::Set) = (length(l) == length(r)) && (l <= r) +< (l::Set, r::Set) = (length(l) < length(r)) && (l <= r) <=(l::Set, r::Set) = issubset(l, r) function issubset(l, r) @@ -112,5 +112,3 @@ function filter!(f::Function, s::Set) return s end filter(f::Function, s::Set) = filter!(f, copy(s)) - -hash(s::Set) = hash(sort(s.dict.keys[s.dict.slots .!= 0])) diff --git a/base/string.jl b/base/string.jl index 3ee7f3fa337ee..1af1db70bfa8f 100644 --- a/base/string.jl +++ b/base/string.jl @@ -482,8 +482,8 @@ function cmp(a::String, b::String) !done(a,i) && done(b,j) ? +1 : 0 end -isequal(a::String, b::String) = cmp(a,b) == 0 -isless(a::String, b::String) = cmp(a,b) < 0 +==(a::String, b::String) = cmp(a,b) == 0 +isless(a::String, b::String) = cmp(a,b) < 0 # begins with and ends with predicates @@ -515,19 +515,18 @@ function endswith(a::String, b::String) end endswith(a::String, c::Char) = !isempty(a) && a[end] == c -# faster comparisons for byte strings +# faster comparisons for byte strings and symbols -cmp(a::ByteString, b::ByteString) = lexcmp(a.data, b.data) -isequal(a::ByteString, b::ByteString) = endof(a)==endof(b) && cmp(a,b)==0 -beginswith(a::ByteString, b::ByteString) = beginswith(a.data, b.data) +cmp(a::ByteString, b::ByteString) = lexcmp(a.data, b.data) +cmp(a::Symbol, b::Symbol) = int(sign(ccall(:strcmp, Int32, (Ptr{Uint8}, Ptr{Uint8}), a, b))) + +==(a::ByteString, b::ByteString) = endof(a) == endof(b) && cmp(a,b) == 0 +isless(a::Symbol, b::Symbol) = cmp(a,b) < 0 +beginswith(a::ByteString, b::ByteString) = beginswith(a.data, b.data) beginswith(a::Array{Uint8,1}, b::Array{Uint8,1}) = (length(a) >= length(b) && ccall(:strncmp, Int32, (Ptr{Uint8}, Ptr{Uint8}, Uint), a, b, length(b)) == 0) -cmp(a::Symbol, b::Symbol) = - int(sign(ccall(:strcmp, Int32, (Ptr{Uint8}, Ptr{Uint8}), a, b))) -isless(a::Symbol, b::Symbol) = cmp(a,b)<0 - # TODO: fast endswith ## character column width function ## @@ -1678,19 +1677,3 @@ pointer{T<:ByteString}(x::SubString{T}, i::Integer) = pointer(x.string.data) + x pointer(x::Union(UTF16String,UTF32String), i::Integer) = pointer(x)+(i-1)*sizeof(eltype(x.data)) pointer{T<:Union(UTF16String,UTF32String)}(x::SubString{T}) = pointer(x.string.data) + x.offset*sizeof(eltype(x.data)) pointer{T<:Union(UTF16String,UTF32String)}(x::SubString{T}, i::Integer) = pointer(x.string.data) + (x.offset + (i-1))*sizeof(eltype(x.data)) - -# string hashing: -if WORD_SIZE == 64 - hash{T<:ByteString}(s::Union(T,SubString{T})) = - ccall(:memhash, Uint64, (Ptr{Void}, Int), pointer(s), sizeof(s)) - hash{T<:ByteString}(s::Union(T,SubString{T}), seed::Union(Int,Uint)) = - ccall(:memhash_seed, Uint64, (Ptr{Void}, Int, Uint32), - pointer(s), sizeof(s), uint32(seed)) -else - hash{T<:ByteString}(s::Union(T,SubString{T})) = - ccall(:memhash32, Uint32, (Ptr{Void}, Int), pointer(s), sizeof(s)) - hash{T<:ByteString}(s::Union(T,SubString{T}), seed::Union(Int,Uint)) = - ccall(:memhash32_seed, Uint32, (Ptr{Void}, Int, Uint32), - pointer(s), sizeof(s), uint32(seed)) -end -hash(s::String) = hash(bytestring(s)) diff --git a/base/sysimg.jl b/base/sysimg.jl index 015c835bb269e..e942a0d8002ae 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -58,6 +58,7 @@ include("bitarray.jl") include("intset.jl") include("dict.jl") include("set.jl") +include("hashing.jl") include("iterator.jl") # compiler @@ -174,6 +175,9 @@ big(q::Rational) = big(num(q))//big(den(q)) big(z::Complex) = complex(big(real(z)),big(imag(z))) @vectorize_1arg Number big +# more hashing definitions +include("hashing2.jl") + # random number generation and statistics include("statistics.jl") include("librandom.jl") diff --git a/base/utf8proc.jl b/base/utf8proc.jl index 32e5d21011b72..d1d7427289d3c 100644 --- a/base/utf8proc.jl +++ b/base/utf8proc.jl @@ -1,7 +1,7 @@ # Various Unicode functionality from the utf8proc library module UTF8proc -import Base: show, showcompact, ==, string, symbol, isless, hash +import Base: show, showcompact, ==, string, symbol, isless # also exported by Base: export normalize_string, is_valid_char, is_assigned_char diff --git a/base/version.jl b/base/version.jl index 9fd46b1821c04..b1b0fa99e6419 100644 --- a/base/version.jl +++ b/base/version.jl @@ -115,7 +115,7 @@ function ident_cmp(A::(Union(Int,ASCIIString)...), !done(A,i) && done(B,j) ? +1 : 0 end -function isequal(a::VersionNumber, b::VersionNumber) +function ==(a::VersionNumber, b::VersionNumber) (a.major != b.major) && return false (a.minor != b.minor) && return false (a.patch != b.patch) && return false @@ -145,7 +145,14 @@ function isless(a::VersionNumber, b::VersionNumber) return false end -hash(v::VersionNumber) = hash([v.(n) for n in VersionNumber.names]) +function hash(v::VersionNumber, h::Uint) + h += uint(0x8ff4ffdb75f9fede) + h = hash(v.major, h) + h = hash(v.minor, h) + h = hash(v.patch, h) + h = hash(v.prerelease, ~h) + h = hash(v.build, ~h) +end lowerbound(v::VersionNumber) = VersionNumber(v.major, v.minor, v.patch, ("",), ()) upperbound(v::VersionNumber) = VersionNumber(v.major, v.minor, v.patch, (), ("",)) diff --git a/doc/stdlib/base.rst b/doc/stdlib/base.rst index a69e266232578..a51e005165166 100644 --- a/doc/stdlib/base.rst +++ b/doc/stdlib/base.rst @@ -139,33 +139,19 @@ All Objects .. function:: isequal(x, y) - True if and only if ``x`` and ``y`` would cause a typical function to behave the - same. A "typical" function is one that uses only intended interfaces, and does not - unreasonably exploit implementation details of its arguments. For example, - floating-point ``NaN`` values are ``isequal`` regardless of their sign bits, since - the sign of a ``NaN`` has no meaning in the vast majority of cases (but can be - discovered if you really want to). - - One implication of this definition is that implementing ``isequal`` for a new type - encapsulates, to a large extent, what the true abstraction presented by that type - is. For example, a ``String`` is a sequence of characters, so two strings are - ``isequal`` if they generate the same characters. Other concerns, such as encoding, - are not considered. - - When calling ``isequal``, be aware that it cannot be all things to all people. - For example, if your use of strings *does* care about encoding, you will have to - perform a check like ``typeof(x)==typeof(y) && isequal(x,y)``. - - ``isequal`` is the default comparison function used by hash tables (``Dict``). - ``isequal(x,y)`` must imply ``hash(x)==hash(y)``. - - New types with a notion of equality should implement this function, except for - numbers, which should implement ``==`` instead. However, numeric types with special - values like ``NaN`` might need to implement ``isequal`` as well. Numbers of different - types are considered unequal. - - Mutable containers should generally implement ``isequal`` by calling ``isequal`` - recursively on all contents. + Similar to ``==``, except treats all floating-point ``NaN`` values as equal to each other, + and treats ``-0.0`` as unequal to ``0.0``. + For values that are not floating-point, ``isequal`` is the same as ``==``. + + ``isequal`` is the comparison function used by hash tables (``Dict``). + ``isequal(x,y)`` must imply that ``hash(x) == hash(y)``. + + Collections typically implement ``isequal`` by calling ``isequal`` recursively on + all contents. + + Scalar types generally do not need to implement ``isequal``, unless they + represent floating-point numbers amenable to a more efficient implementation + than that provided as a generic fallback (based on ``isnan``, ``signbit``, and ``==``). .. function:: isless(x, y) @@ -2292,8 +2278,18 @@ Mathematical Operators .. _==: .. function:: ==(x, y) - Numeric equality operator. Compares numbers and number-like values (e.g. arrays) by numeric value. True for numbers of different types that represent the same value (e.g. ``2`` and ``2.0``). Follows IEEE semantics for floating-point numbers. - New numeric types should implement this function for two arguments of the new type. + Generic equality operator, giving a single ``Bool`` result. Falls back to ``===``. + Should be implemented for all types with a notion of equality, based + on the abstract value that an instance represents. For example, all numeric types are compared + by numeric value, ignoring type. Strings are compared as sequences of characters, ignoring + encoding. + + Follows IEEE semantics for floating-point numbers. + + Collections should generally implement ``==`` by calling ``==`` recursively on all contents. + + New numeric types should implement this function for two arguments of the new type, and handle + comparison to other types via promotion rules where possible. .. _!=: .. function:: !=(x, y) diff --git a/test/arrayops.jl b/test/arrayops.jl index 33c3f2254c7ba..ddcbb733d025c 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -363,7 +363,7 @@ D = cat(3, B, B) immutable HashCollision x::Float64 end -Base.hash(::HashCollision) = uint(0) +Base.hash(::HashCollision, h::Uint) = h @test map(x->x.x, unique(map(HashCollision, B), 1)) == C ## reduce ## diff --git a/test/collections.jl b/test/collections.jl index 8e30731e1ee93..3a4bd61275e07 100644 --- a/test/collections.jl +++ b/test/collections.jl @@ -80,7 +80,7 @@ type I1438T id end import Base.hash -hash(x::I1438T) = x.id +hash(x::I1438T, h::Uint) = hash(x.id, h) begin local seq, xs, s diff --git a/test/hashing.jl b/test/hashing.jl index f713ccd929131..6b2b767e54229 100644 --- a/test/hashing.jl +++ b/test/hashing.jl @@ -35,7 +35,3 @@ end @test hash(:(X.x)) != hash(:(X.y)) @test hash([1,2]) == hash(sub([1,2,3,4],1:2)) - -# make sure >>> is used -@test bitmix(2, -3) != bitmix(2, -4) -@test bitmix(-3, 2) != bitmix(-4, 2) diff --git a/test/numbers.jl b/test/numbers.jl index d2fd2fc479b23..2afa54cb0fa37 100644 --- a/test/numbers.jl +++ b/test/numbers.jl @@ -382,8 +382,8 @@ end @test !isequal(+1.0,-1.0) @test !isequal(+Inf,-Inf) -@test !isequal(-0.0f0,-0.0) -@test !isequal( 0.0f0, 0.0) +@test isequal(-0.0f0,-0.0) +@test isequal( 0.0f0, 0.0) @test !isequal(-0.0f0, 0.0) @test !isequal(0.0f0 ,-0.0) @@ -459,8 +459,8 @@ end @test !isless(+NaN,-NaN) @test !isless(+NaN,+NaN) -@test !isequal( 0, 0.0) -@test !isequal( 0.0, 0) +@test isequal( 0, 0.0) +@test isequal( 0.0, 0) @test !isequal( 0,-0.0) @test !isequal(-0.0, 0) @test isless(-0.0, 0) diff --git a/test/ranges.jl b/test/ranges.jl index 0c947739c3e42..8379eaab90218 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -245,12 +245,10 @@ let for r in Rs ar = collect(r) @test r != ar - @test !isequal(r, ar) + @test !isequal(r,ar) for s in Rs as = collect(s) - - @test !isequal(r, s) || hash(r)==hash(s) - + @test !isequal(r,s) || hash(r)==hash(s) @test (r==s) == (ar==as) end end