Skip to content

Commit a38c088

Browse files
committed
move hashing2 functions to their relevant files [NFC]
1 parent 24dde5d commit a38c088

File tree

7 files changed

+238
-244
lines changed

7 files changed

+238
-244
lines changed

base/Base.jl

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -324,9 +324,6 @@ using .MPFR
324324

325325
include("combinatorics.jl")
326326

327-
# more hashing definitions
328-
include("hashing2.jl")
329-
330327
# irrational mathematical constants
331328
include("irrationals.jl")
332329
include("mathconstants.jl")

base/float.jl

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,110 @@ end
484484
hash(x::Float32, h::UInt) = hash(Float64(x), h)
485485
hash(x::Float16, h::UInt) = hash(Float64(x), h)
486486

487+
## generic hashing for rational values ##
488+
489+
function hash(x::Real, h::UInt)
490+
# decompose x as num*2^pow/den
491+
num, pow, den = decompose(x)
492+
493+
# handle special values
494+
num == 0 && den == 0 && return hash(NaN, h)
495+
num == 0 && return hash(ifelse(den > 0, 0.0, -0.0), h)
496+
den == 0 && return hash(ifelse(num > 0, Inf, -Inf), h)
497+
498+
# normalize decomposition
499+
if den < 0
500+
num = -num
501+
den = -den
502+
end
503+
z = trailing_zeros(num)
504+
if z != 0
505+
num >>= z
506+
pow += z
507+
end
508+
z = trailing_zeros(den)
509+
if z != 0
510+
den >>= z
511+
pow -= z
512+
end
513+
514+
# handle values representable as Int64, UInt64, Float64
515+
if den == 1
516+
left = ndigits0z(num,2) + pow
517+
right = trailing_zeros(num) + pow
518+
if -1074 <= right
519+
if 0 <= right && left <= 64
520+
left <= 63 && return hash(Int64(num) << Int(pow), h)
521+
signbit(num) == signbit(den) && return hash(UInt64(num) << Int(pow), h)
522+
end # typemin(Int64) handled by Float64 case
523+
left <= 1024 && left - right <= 53 && return hash(ldexp(Float64(num),pow), h)
524+
end
525+
end
526+
527+
# handle generic rational values
528+
h = hash_integer(den, h)
529+
h = hash_integer(pow, h)
530+
h = hash_integer(num, h)
531+
return h
532+
end
533+
534+
#=
535+
`decompose(x)`: non-canonical decomposition of rational values as `num*2^pow/den`.
536+
537+
The decompose function is the point where rational-valued numeric types that support
538+
hashing hook into the hashing protocol. `decompose(x)` should return three integer
539+
values `num, pow, den`, such that the value of `x` is mathematically equal to
540+
541+
num*2^pow/den
542+
543+
The decomposition need not be canonical in the sense that it just needs to be *some*
544+
way to express `x` in this form, not any particular way – with the restriction that
545+
`num` and `den` may not share any odd common factors. They may, however, have powers
546+
of two in common – the generic hashing code will normalize those as necessary.
547+
548+
Special values:
549+
550+
- `x` is zero: `num` should be zero and `den` should have the same sign as `x`
551+
- `x` is infinite: `den` should be zero and `num` should have the same sign as `x`
552+
- `x` is not a number: `num` and `den` should both be zero
553+
=#
554+
555+
decompose(x::Integer) = x, 0, 1
556+
557+
function decompose(x::Float16)::NTuple{3,Int}
558+
isnan(x) && return 0, 0, 0
559+
isinf(x) && return ifelse(x < 0, -1, 1), 0, 0
560+
n = reinterpret(UInt16, x)
561+
s = (n & 0x03ff) % Int16
562+
e = ((n & 0x7c00) >> 10) % Int
563+
s |= Int16(e != 0) << 10
564+
d = ifelse(signbit(x), -1, 1)
565+
s, e - 25 + (e == 0), d
566+
end
567+
568+
function decompose(x::Float32)::NTuple{3,Int}
569+
isnan(x) && return 0, 0, 0
570+
isinf(x) && return ifelse(x < 0, -1, 1), 0, 0
571+
n = reinterpret(UInt32, x)
572+
s = (n & 0x007fffff) % Int32
573+
e = ((n & 0x7f800000) >> 23) % Int
574+
s |= Int32(e != 0) << 23
575+
d = ifelse(signbit(x), -1, 1)
576+
s, e - 150 + (e == 0), d
577+
end
578+
579+
function decompose(x::Float64)::Tuple{Int64, Int, Int}
580+
isnan(x) && return 0, 0, 0
581+
isinf(x) && return ifelse(x < 0, -1, 1), 0, 0
582+
n = reinterpret(UInt64, x)
583+
s = (n & 0x000fffffffffffff) % Int64
584+
e = ((n & 0x7ff0000000000000) >> 52) % Int
585+
s |= Int64(e != 0) << 52
586+
d = ifelse(signbit(x), -1, 1)
587+
s, e - 1075 + (e == 0), d
588+
end
589+
590+
487591
"""
488592
precision(num::AbstractFloat)
489593

base/gmp.jl

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ module MPZ
132132
# - a method modifying its input has a "!" appendend to its name, according to Julia's conventions
133133
# - some convenient methods are added (in addition to the pure MPZ ones), e.g. `add(a, b) = add!(BigInt(), a, b)`
134134
# and `add!(x, a) = add!(x, x, a)`.
135-
using .Base.GMP: BigInt, Limb, BITS_PER_LIMB
135+
using ..GMP: BigInt, Limb, BITS_PER_LIMB
136136

137137
const mpz_t = Ref{BigInt}
138138
const bitcnt_t = Culong
@@ -764,4 +764,77 @@ function Base.deepcopy_internal(x::BigInt, stackdict::IdDict)
764764
return y
765765
end
766766

767+
## streamlined hashing for BigInt, by avoiding allocation from shifts ##
768+
769+
if Limb === UInt
770+
# this condition is true most (all?) of the time, and in this case we can define
771+
# an optimized version of the above hash_integer(::Integer, ::UInt) method for BigInt
772+
# used e.g. for Rational{BigInt}
773+
function hash_integer(n::BigInt, h::UInt)
774+
GC.@preserve n begin
775+
s = n.size
776+
s == 0 && return hash_integer(0, h)
777+
p = convert(Ptr{UInt}, n.d)
778+
b = unsafe_load(p)
779+
h ⊻= hash_uint(ifelse(s < 0, -b, b) h)
780+
for k = 2:abs(s)
781+
h ⊻= hash_uint(unsafe_load(p, k) h)
782+
end
783+
return h
784+
end
785+
end
786+
787+
_divLimb(n) = UInt === UInt64 ? n >>> 6 : n >>> 5
788+
_modLimb(n) = UInt === UInt64 ? n & 63 : n & 31
789+
790+
function hash(x::BigInt, h::UInt)
791+
GC.@preserve x begin
792+
sz = x.size
793+
sz == 0 && return hash(0, h)
794+
ptr = Ptr{UInt}(x.d)
795+
if sz == 1
796+
return hash(unsafe_load(ptr), h)
797+
elseif sz == -1
798+
limb = unsafe_load(ptr)
799+
limb <= typemin(Int) % UInt && return hash(-(limb % Int), h)
800+
end
801+
pow = trailing_zeros(x)
802+
nd = ndigits0z(x, 2)
803+
idx = _divLimb(pow) + 1
804+
shift = _modLimb(pow) % UInt
805+
upshift = BITS_PER_LIMB - shift
806+
asz = abs(sz)
807+
if shift == 0
808+
limb = unsafe_load(ptr, idx)
809+
else
810+
limb1 = unsafe_load(ptr, idx)
811+
limb2 = idx < asz ? unsafe_load(ptr, idx+1) : UInt(0)
812+
limb = limb2 << upshift | limb1 >> shift
813+
end
814+
if nd <= 1024 && nd - pow <= 53
815+
return hash(ldexp(flipsign(Float64(limb), sz), pow), h)
816+
end
817+
h = hash_integer(1, h)
818+
h = hash_integer(pow, h)
819+
h ⊻= hash_uint(flipsign(limb, sz) h)
820+
for idx = idx+1:asz
821+
if shift == 0
822+
limb = unsafe_load(ptr, idx)
823+
else
824+
limb1 = limb2
825+
if idx == asz
826+
limb = limb1 >> shift
827+
limb == 0 && break # don't hash leading zeros
828+
else
829+
limb2 = unsafe_load(ptr, idx+1)
830+
limb = limb2 << upshift | limb1 >> shift
831+
end
832+
end
833+
h ⊻= hash_uint(limb h)
834+
end
835+
return h
836+
end
837+
end
838+
end
839+
767840
end # module

base/hashing.jl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,23 @@ else
6666
hash_uint(x::UInt) = hash_32_32(x)
6767
end
6868

69+
## efficient value-based hashing of integers ##
70+
71+
hash(x::Int64, h::UInt) = hash_uint64(bitcast(UInt64, x)) - 3h
72+
hash(x::UInt64, h::UInt) = hash_uint64(x) - 3h
73+
hash(x::Union{Bool,Int8,UInt8,Int16,UInt16,Int32,UInt32}, h::UInt) = hash(Int64(x), h)
74+
75+
function hash_integer(n::Integer, h::UInt)
76+
h ⊻= hash_uint((n % UInt) h)
77+
n = abs(n)
78+
n >>>= sizeof(UInt) << 3
79+
while n != 0
80+
h ⊻= hash_uint((n % UInt) h)
81+
n >>>= sizeof(UInt) << 3
82+
end
83+
return h
84+
end
85+
6986
## symbol & expression hashing ##
7087

7188
if UInt === UInt64

0 commit comments

Comments
 (0)