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

Add radix sort #44230

Merged
merged 103 commits into from
Apr 3, 2022
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
103 commits
Select commit Hold shift + click to select a range
ee3964b
Add radix sort
LilithHafner Feb 17, 2022
569a5c1
Try to fix build error (1)
LilithHafner Feb 18, 2022
67b744c
Try to fix build error (2)
LilithHafner Feb 18, 2022
b8ad40f
Try to fix build error (3)
LilithHafner Feb 18, 2022
5fd8e42
Try to fix build error (4)
LilithHafner Feb 18, 2022
7cf473d
Try to fix build error (5)
LilithHafner Feb 18, 2022
d8363a5
Try to fix build error (6)
LilithHafner Feb 18, 2022
44b8411
@LilithHafner Try to fix build error (7)
LilithHafner Feb 18, 2022
a22b120
Try to fix build error (8)
LilithHafner Feb 18, 2022
f0d6536
Remove set default for debugging
LilithHafner Feb 19, 2022
c42ec18
Update sort_int_range! tests
LilithHafner Feb 19, 2022
fc0e7bb
put back update defalg after bootstrapping (ugly)
LilithHafner Feb 19, 2022
7b665ef
update usage of sort_int_range! in test to add lo, hi
LilithHafner Feb 20, 2022
0c2e202
whitespace
LilithHafner Feb 20, 2022
44e4ea0
add RadixSort tests
LilithHafner Feb 20, 2022
9f83339
more radix sort tests
LilithHafner Feb 20, 2022
39216ad
fix typo
LilithHafner Feb 20, 2022
e045255
maintain backwards compatability in sort_int_range
LilithHafner Feb 20, 2022
3747e79
fix maybereverse use
LilithHafner Feb 20, 2022
2fc70fb
remove set defalg
LilithHafner Feb 22, 2022
fa198ca
Merge branch 'JuliaLang:master' into patch-9
LilithHafner Feb 22, 2022
c692830
put back set defalg
LilithHafner Feb 22, 2022
7f2b076
dispatch to InsertionSort _before_ fpsort!ing
LilithHafner Feb 23, 2022
9d3b5d5
move set defalg earlier in Base.jjl to set up for potetial bisection
LilithHafner Feb 23, 2022
3e533cd
put defalg back together!
LilithHafner Feb 23, 2022
0d40342
bisection 1
LilithHafner Feb 23, 2022
626f8bc
bisection 2
LilithHafner Feb 23, 2022
6104982
bisection 3 (final bisection if tests pass)
LilithHafner Feb 23, 2022
31aae4d
bisection 4 (final bisection if tests pass)
LilithHafner Feb 23, 2022
8a8f239
Add specialized sorting for Bool
LilithHafner Feb 23, 2022
39a51cf
Immediately dispatch to sort_int_range! for long Vector{[U]Int8}
LilithHafner Feb 23, 2022
10e4204
fix typo, lol
LilithHafner Feb 23, 2022
2dfaa2d
test and fix boolean sorting
LilithHafner Feb 23, 2022
045869d
fix overflow performance bug that prevented edge case dispatch to sor…
LilithHafner Feb 23, 2022
3abefaa
restrict fpsort! policy on perm orderings to exclude Bool
LilithHafner Feb 23, 2022
758c026
fix typo
LilithHafner Feb 23, 2022
85fed57
switch from custom Bool sort to re-use of sort_int_range! (faster)
LilithHafner Feb 23, 2022
9a3f1c7
rename RadixSort to AdaptiveSort
LilithHafner Feb 23, 2022
3af1412
also rename in tests
LilithHafner Feb 24, 2022
98aaa97
add new boolean sorting (instead of slower dispatch to sort_int_range!)
LilithHafner Feb 26, 2022
0a93e4b
rework dispatch (and remove Some from Serializable)
LilithHafner Feb 27, 2022
f4b3e3b
spelling and minor comments
LilithHafner Feb 27, 2022
59706ad
fix typo and only unsign when possible
LilithHafner Feb 27, 2022
c471af9
bump to rerun CI
LilithHafner Mar 1, 2022
82e8f26
undo bump to rerun CI
LilithHafner Mar 1, 2022
41490eb
update comment to explain defalg redefinition
LilithHafner Mar 1, 2022
6eb4d77
Merge branch 'JuliaLang:master' into patch-9
LilithHafner Mar 1, 2022
592b814
micro optimization
LilithHafner Mar 1, 2022
c5f0bf1
Style
LilithHafner Mar 2, 2022
d88230b
new dispatch (before exterma-serialization separation
LilithHafner Mar 3, 2022
ffcc043
Seperate extrema and serialization in all cases
LilithHafner Mar 4, 2022
5f9c0fc
Minor simplification (maybe helps performance 1-2%)
LilithHafner Mar 4, 2022
84cedaa
Add comments to radaix_sort! and make computationally negligable chan…
LilithHafner Mar 4, 2022
9283d75
Update docstrings & comments
LilithHafner Mar 4, 2022
06cfaae
finish type change: unsigned(eltype(hi-lo)) -> UInt
LilithHafner Mar 4, 2022
291399e
minor comment style
LilithHafner Mar 4, 2022
1195798
reorder for readability
LilithHafner Mar 5, 2022
338f3f5
Integrate with compiler bootstrapping & remove defalg redefinitions!
LilithHafner Mar 5, 2022
a186c7f
Remove unhelpful comment
LilithHafner Mar 5, 2022
8896af2
remove stray newline
LilithHafner Mar 5, 2022
27b1656
Code review based on PR diff
LilithHafner Mar 5, 2022
5f01201
Spelling
LilithHafner Mar 5, 2022
f978728
Qualify DEFAULT_(UN)STABLE in tests
LilithHafner Mar 5, 2022
f03e4c5
Comments and such
LilithHafner Mar 5, 2022
cf04241
Rename variables
LilithHafner Mar 11, 2022
96c0b6e
Find, test & fix bug
LilithHafner Mar 11, 2022
67494a5
Update base/sort.jl
LilithHafner Mar 12, 2022
c344e82
Update base/sort.jl
LilithHafner Mar 13, 2022
68fef15
Update base/sort.jl
LilithHafner Mar 13, 2022
a94cfea
add explain small fallback in fpsort!
LilithHafner Mar 13, 2022
8d844bf
add @inbounds to _extrema
LilithHafner Mar 13, 2022
222f8e3
multiline -= u_min
LilithHafner Mar 13, 2022
a7d7f56
add
LilithHafner Mar 13, 2022
34c700c
Apply suggestions from code review
LilithHafner Mar 13, 2022
18c56a9
replace list with vector in comments
LilithHafner Mar 13, 2022
09162ae
remove unneccesary comment
LilithHafner Mar 13, 2022
5473b3c
Comment on count sort for Bool and add `@inbounds`
LilithHafner Mar 13, 2022
92f69fa
remove unused bool serialization
LilithHafner Mar 13, 2022
6275928
bugfix: add using @assert
LilithHafner Mar 13, 2022
5eca90a
simplify float serailization
LilithHafner Mar 14, 2022
17f45e8
Remove compilation tricks and put upper limit on chunk_size
LilithHafner Mar 14, 2022
32a3197
ckeck for ordered inputs
LilithHafner Mar 14, 2022
4931881
use view not macro view
LilithHafner Mar 14, 2022
4d2e184
decouple _extrema and out_of_order to eliminate overhead (and optimiz…
LilithHafner Mar 14, 2022
de33146
put back all same check (free)
LilithHafner Mar 15, 2022
5ff3f07
Update heuristic
LilithHafner Mar 15, 2022
ef24550
Move second dispatch to insertion sort to after presorted detection
LilithHafner Mar 15, 2022
dad6a26
Change length threshold from len > 61 to len > 60 for consistency
LilithHafner Mar 16, 2022
0bf35ab
Move submodule Sort.Serial to Sort and rename `serial` to `uint_map`
LilithHafner Mar 16, 2022
c80b3cd
Docstring
LilithHafner Mar 16, 2022
9a626d7
Try to fix build error (1)
LilithHafner Mar 16, 2022
701aa0a
Defer `bits` calculation till needed for readability
LilithHafner Mar 16, 2022
693adab
make floats UIntMappable, lol
LilithHafner Mar 16, 2022
563a816
Test Coverage
LilithHafner Mar 16, 2022
dceee48
Explain floating point exceptions
LilithHafner Mar 16, 2022
29442e7
Fix argument order in docstrings
LilithHafner Mar 16, 2022
5f7d0d9
Merge branch 'JuliaLang:master' into patch-9
LilithHafner Mar 16, 2022
070a48e
Fix argument order in docstrings part 2
LilithHafner Mar 16, 2022
287013c
Fix typo in tests
LilithHafner Mar 17, 2022
0581855
Replace partially presorted check with fully presorted check
LilithHafner Mar 23, 2022
c07fb05
Apply suggestions from code review
LilithHafner Mar 31, 2022
d6087d0
adjust presorted checks
LilithHafner Mar 31, 2022
609abee
proof read
LilithHafner Apr 2, 2022
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
161 changes: 78 additions & 83 deletions base/sort.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ using .Base: copymutable, LinearIndices, length, (:),
AbstractMatrix, AbstractUnitRange, isless, identity, eltype, >, <, <=, >=, |, +, -, *, !,
extrema, sub_with_overflow, add_with_overflow, oneunit, div, getindex, setindex!,
length, resize!, fill, Missing, require_one_based_indexing, keytype,
UnitRange, min, max, Val, unsigned, @assert
UnitRange, min, max, reinterpret, signed, unsigned, Signed, Unsigned, typemin, xor, Type

using .Base: >>>, !==

Expand Down Expand Up @@ -429,16 +429,15 @@ struct MergeSortAlg <: Algorithm end
"""
AdaptiveSort(fallback)

Indicate that a sorting function should pick the fastest available algorithm for the
given element type, order, length, and contents. If no alternatives are viable, uses
the `fallback` algorithm. AdaptiveSort is stable if `fallback` is stable.
Indicate that a sorting function should use the fastest available algorithm.

Algorithms currently in use:
Adaptive sort will use the algorithm specified by `fallback` for types and orders that are
not [`UIntMappable`](@ref). Otherwise, it will typically use:
* Insertion sort for short vectors
* Radix sort for long vectors
* Counting sort for vectors of integers spanning a short range
* Fallback algorithm when the input cannot be efficiently
converted into a vector of integers maintaining sort order

Adaptive sort is guaranteed to be stable if the fallback algorithm is stable.
"""
struct AdaptiveSort{Fallback <: Algorithm} <: Algorithm
fallback::Fallback
Expand Down Expand Up @@ -762,25 +761,29 @@ function _extrema(v::AbstractArray, lo::Integer, hi::Integer, o::Ordering)
mn, mx
end
function sort!(v::AbstractVector, lo::Integer, hi::Integer, a::AdaptiveSort, o::Ordering)
# if the sorting task is unserializable, then we can't radix sort or sort_int_range!
# if the sorting task is not UIntMappable, then we can't radix sort or sort_int_range!
# so we skip straight to the fallback algorithm which is comparison based.
U = Serial.Serializable(eltype(v), o)
U = UIntMappable(eltype(v), o)
U === nothing && return sort!(v, lo, hi, a.fallback, o)

# to avoid introducing excessive detection costs for the trivial sorting problem
# and to avoid overflow, we check for small inputs before any other runtime checks
hi <= lo && return v
lenm1 = maybe_unsigned(hi-lo) # adding 1 would risk overflow
# only count sort on a short range can compete with insertion sort fo lenm1 < 30
# only count sort on a short range can compete with insertion sort fo lenm1 < 40
# and the optimization is not worth the detection cost, so we use insertion sort.
lenm1 < 30 && return sort!(v, lo, hi, SMALL_ALGORITHM, o)
lenm1 < 40 && return sort!(v, lo, hi, SMALL_ALGORITHM, o)

# For most long arrays, a presorted check is essentially free (overhead < 1%)
lenm1 >= 200 && issorted(view(v, lo:hi), o) && return v

# UInt128 does not support fast bit shifting so we never
# dispatch to radix sort but we may still perform count sort
if sizeof(U) > 8
if eltype(v) <: Integer && o isa DirectOrdering
v_min, v_max = _extrema(v, lo, hi, Forward)
v_range = maybe_unsigned(v_max-v_min)
v_range == 0 && return v # all same

# we know lenm1 ≥ 30, so this will never underflow.
# if lenm1 > 3.7e18 (59 exabytes), then this may incorrectly dispatch to fallback
Expand All @@ -792,6 +795,7 @@ function sort!(v::AbstractVector, lo::Integer, hi::Integer, a::AdaptiveSort, o::
end

v_min, v_max = _extrema(v, lo, hi, o)
lt(o, v_min, v_max) || return v # all same
if eltype(v) <: Integer && o isa DirectOrdering
R = o === Reverse
v_range = maybe_unsigned(R ? v_min-v_max : v_max-v_min)
nalimilan marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -800,8 +804,13 @@ function sort!(v::AbstractVector, lo::Integer, hi::Integer, a::AdaptiveSort, o::
end
end

u_min, u_max = Serial.serialize(v_min, o), Serial.serialize(v_max, o)
u_min, u_max = uint_map(v_min, o), uint_map(v_max, o)
u_range = maybe_unsigned(u_max-u_min)
if u_range < div(lenm1, 2) # count sort will be superior if u's range is very small
u = uint_map!(v, lo, hi, o)
sort_int_range!(u, Int(u_range+1), u_min, identity, lo, hi)
return uint_unmap!(v, u, lo, hi, o)
end

# if u's range is small, then once we subtract out v_min, we'll get a vector like
# UInt16[0x001a, 0x0015, 0x0006, 0x001b, 0x0008, 0x000c, 0x0001, 0x000e, 0x001c, 0x0009]
Expand All @@ -815,28 +824,20 @@ function sort!(v::AbstractVector, lo::Integer, hi::Integer, a::AdaptiveSort, o::
# Insertion < Radix
# lenm1^2 < 3 * bits * lenm1
# lenm1 < 3bits
lenm1 < 3bits && return sort!(v, lo, hi, SMALL_ALGORITHM, o)
# at lenm1 = 64*3-1, QuickSort is about 20% faster than InsertionSort. The window
# where QuickSort is superior is the triangle defined by (lenm1=128, bits=43),
# (lenm1=191, bits=64), and (lenm1=128, bits=64). This is a small window, spanning only
# .015 square orders of magnitude, and the 20% performance gap is only present at
# the apex. At the centroid, the gap is about 6%. On the other hand, there are
# theoretical/compilation benefits to avoiding the fallback entirely for small
# serializable types and orderings, so we unconditionally use InsertionSort.

u = Serial.serialize!(v, lo, hi, o)

if u_range < div(lenm1, 2) # count sort will be superior if u's range is very small
sort_int_range!(u, Int(u_range+1), u_min, identity, lo, hi)
return Serial.deserialize!(v, u, lo, hi, o)
if lenm1 < 3bits
# at lenm1 = 64*3-1, QuickSort is about 20% faster than InsertionSort.
alg = a.fallback === QuickSort && lenm1 > 120 ? QuickSort : SMALL_ALGORITHM
return sort!(v, lo, hi, alg, o)
end

# At this point, we are committed to radix sort.
u = uint_map!(v, lo, hi, o)

# we subtract u_min to avoid radixing over unnecessary bits. For example,
# Int32[3, -1, 2] serializes to UInt32[0x80000003, 0x7fffffff, 0x80000002]
# Int32[3, -1, 2] uint_maps to UInt32[0x80000003, 0x7fffffff, 0x80000002]
# which uses all 32 bits, but once we subtract u_min = 0x7fffffff, we are left with
# UInt32[0x00000004, 0x00000000, 0x00000003] which uses only 3 bits, and
# Float32[2.012, 400.0, 12.345] serializes to UInt32[0x3fff3b63, 0x3c37ffff, 0x414570a4]
# Float32[2.012, 400.0, 12.345] uint_maps to UInt32[0x3fff3b63, 0x3c37ffff, 0x414570a4]
# which is reduced to UInt32[0x03c73b64, 0x00000000, 0x050d70a5] using only 26 bits.
# the overhead for this subtraction is small enough that it is worthwhile in many cases.
# this is faster than u[lo:hi] .-= u_min
Expand All @@ -845,7 +846,7 @@ function sort!(v::AbstractVector, lo::Integer, hi::Integer, a::AdaptiveSort, o::
end

u2 = radix_sort!(u, lo, hi, bits, similar(u))
Serial.deserialize!(v, u2, lo, hi, o, u_min)
uint_unmap!(v, u2, lo, hi, o, u_min)
end

## generic sorting methods ##
Expand Down Expand Up @@ -1293,110 +1294,104 @@ function sort!(A::AbstractArray;
A
end

## sorting serialization to alow radix sorting primitives other than UInts ##
module Serial
using ...Order
using ..Base: @inbounds, @eval, AbstractVector, nothing, signed, unsigned,
typemin, xor, reinterpret, Signed, Unsigned, Type, eltype

## uint mapping to alow radix sorting primitives other than UInts ##
LilithHafner marked this conversation as resolved.
Show resolved Hide resolved

"""
Serializable(T::Type, order::Ordering)
UIntMappable(T::Type, order::Ordering)

Return `typeof(serialize(x::T, order))` if [`serialize`](@ref) and
[`deserialize`](@ref) are implemented.
Return `typeof(uint_map(x::T, order))` if [`uint_map`](@ref) and
[`uint_unmap`](@ref) are implemented.

If either is not implemented, return `nothing`.
"""
Serializable(T::Type, order::Ordering) = nothing
UIntMappable(T::Type, order::Ordering) = nothing

"""
serialize(x, order::Ordering)::Unsigned
uint_map(x, order::Ordering)::Unsigned

Map `x` to an un unsigned integer, maintaining sort order.

The map should be reversible with [`deserialize`](@ref), so `isless(order, a, b)` must be
The map should be reversible with [`uint_unmap`](@ref), so `isless(order, a, b)` must be
a linear ordering for `a, b <: typeof(x)`. Satisfies
`isless(order, a, b) === (serialize(order, a) < serialize(order, b))`
and `x === deserialize(typeof(x), serialize(order, x), order)`
`isless(order, a, b) === (uint_map(a, order) < uint_map(b, order))`
and `x === uint_unmap(typeof(x), uint_map(x, order), order)`

See also: [`Serializable`](@ref) [`deserialize`](@ref)
See also: [`UIntMappable`](@ref) [`uint_unmap`](@ref)
"""
function serialize end
function uint_map end

"""
deserialize(T::Type, u::Unsigned, order::Ordering)
uint_unmap(T::Type, u::Unsigned, order::Ordering)

Reconstruct the unique value `x::T` that serializes to `u`. Satisfies
`x === deserialize(T, order, serialize(order, x::T))` for all `x <: T`.
Reconstruct the unique value `x::T` that uint_maps to `u`. Satisfies
`x === uint_unmap(T, uint_map(x::T, order), order)` for all `x <: T`.

See also: [`serialize`](@ref) [`Serializable`](@ref)
See also: [`uint_map`](@ref) [`UIntMappable`](@ref)
"""
function deserialize end
function uint_unmap end


### Primitive Types

# Integers
serialize(x::Unsigned, ::ForwardOrdering) = x
deserialize(::Type{T}, u::T, ::ForwardOrdering) where T <: Unsigned = u
uint_map(x::Unsigned, ::ForwardOrdering) = x
uint_unmap(::Type{T}, u::T, ::ForwardOrdering) where T <: Unsigned = u

serialize(x::Signed, ::ForwardOrdering) =
uint_map(x::Signed, ::ForwardOrdering) =
unsigned(xor(x, typemin(x)))
deserialize(::Type{T}, u::Unsigned, ::ForwardOrdering) where T <: Signed =
uint_unmap(::Type{T}, u::Unsigned, ::ForwardOrdering) where T <: Signed =
xor(signed(u), typemin(T))

# unsigned(Int) is not available during bootstrapping.
for (U, S) in [(UInt8, Int8), (UInt16, Int16), (UInt32, Int32), (UInt64, Int64), (UInt128, Int128)]
@eval Serializable(::Type{<:Union{$U, $S}}, ::ForwardOrdering) = $U
@eval UIntMappable(::Type{<:Union{$U, $S}}, ::ForwardOrdering) = $U
end

# Floats are not Serializable under regular orderings because they fail on NaN edge cases.
# Float serialization is defined in ..Float, where the Left and Right orderings guarantee
# that there are no NaN values
# Floats are not UIntMappable under regular orderings because they fail on NaN edge cases.
# uint mappings for floats are defined in Float, where the Left and Right orderings
# guarantee that there are no NaN values

# Chars
serialize(x::Char, ::ForwardOrdering) = reinterpret(UInt32, x)
deserialize(::Type{Char}, u::UInt32, ::ForwardOrdering) = reinterpret(Char, u)
Serializable(::Type{Char}, ::ForwardOrdering) = UInt32
uint_map(x::Char, ::ForwardOrdering) = reinterpret(UInt32, x)
uint_unmap(::Type{Char}, u::UInt32, ::ForwardOrdering) = reinterpret(Char, u)
UIntMappable(::Type{Char}, ::ForwardOrdering) = UInt32

### Reverse orderings
serialize(x, rev::ReverseOrdering) = ~serialize(x, rev.fwd)
deserialize(T::Type, u::Unsigned, rev::ReverseOrdering) = deserialize(T, ~u, rev.fwd)
Serializable(T::Type, order::ReverseOrdering) = Serializable(T, order.fwd)
uint_map(x, rev::ReverseOrdering) = ~uint_map(x, rev.fwd)
uint_unmap(T::Type, u::Unsigned, rev::ReverseOrdering) = uint_unmap(T, ~u, rev.fwd)
UIntMappable(T::Type, order::ReverseOrdering) = UIntMappable(T, order.fwd)


### Vectors

# Convert v to unsigned integers in place, maintaining sort order.
function serialize!(v::AbstractVector, lo::Integer, hi::Integer, order::Ordering)
u = reinterpret(Serializable(eltype(v), order), v)
function uint_map!(v::AbstractVector, lo::Integer, hi::Integer, order::Ordering)
u = reinterpret(UIntMappable(eltype(v), order), v)
@inbounds for i in lo:hi
u[i] = serialize(v[i], order)
u[i] = uint_map(v[i], order)
end
u
end

function deserialize!(v::AbstractVector, u::AbstractVector{U}, lo::Integer, hi::Integer,
function uint_unmap!(v::AbstractVector, u::AbstractVector{U}, lo::Integer, hi::Integer,
order::Ordering, offset::U=zero(U)) where U <: Unsigned
LilithHafner marked this conversation as resolved.
Show resolved Hide resolved
@inbounds for i in lo:hi
v[i] = deserialize(eltype(v), u[i]+offset, order)
v[i] = uint_unmap(eltype(v), u[i]+offset, order)
end
v
end

end # module Sort.Serial


## fast clever sorting for floats ##

module Float
using ..Sort
using ..Sort.Serial
using ...Order
using ..Base: @inbounds, AbstractVector, Vector, last, axes, Missing, Type, reinterpret

import Core.Intrinsics: slt_int
import ..Sort: sort!
import ..Sort: sort!, UIntMappable, uint_map, uint_unmap
import ...Order: lt, DirectOrdering

const Floats = Union{Float32,Float64}
Expand All @@ -1419,17 +1414,17 @@ right(o::Perm) = Perm(right(o.order), o.data)
lt(::Left, x::T, y::T) where {T<:Floats} = slt_int(y, x)
lt(::Right, x::T, y::T) where {T<:Floats} = slt_int(x, y)

Serial.serialize(x::Float32, ::Left) = ~reinterpret(UInt32, x)
Serial.deserialize(::Type{Float32}, u::UInt32, ::Left) = reinterpret(Float32, ~u)
Serial.serialize(x::Float32, ::Right) = reinterpret(UInt32, x)
Serial.deserialize(::Type{Float32}, u::UInt32, ::Right) = reinterpret(Float32, u)
Serial.Serializable(::Type{Float32}, ::Union{Left, Right}) = UInt32

Serial.serialize(x::Float64, ::Left) = ~reinterpret(UInt64, x)
Serial.deserialize(::Type{Float64}, u::UInt64, ::Left) = reinterpret(Float64, ~u)
Serial.serialize(x::Float64, ::Right) = reinterpret(UInt64, x)
Serial.deserialize(::Type{Float64}, u::UInt64, ::Right) = reinterpret(Float64, u)
Serial.Serializable(::Type{Float64}, ::Union{Left, Right}) = UInt64
uint_map(x::Float32, ::Left) = ~reinterpret(UInt32, x)
uint_unmap(::Type{Float32}, u::UInt32, ::Left) = reinterpret(Float32, ~u)
uint_map(x::Float32, ::Right) = reinterpret(UInt32, x)
uint_unmap(::Type{Float32}, u::UInt32, ::Right) = reinterpret(Float32, u)
UIntMappable(::Type{Float32}, ::Union{Left, Right}) = UInt32

uint_map(x::Float64, ::Left) = ~reinterpret(UInt64, x)
uint_unmap(::Type{Float64}, u::UInt64, ::Left) = reinterpret(Float64, ~u)
uint_map(x::Float64, ::Right) = reinterpret(UInt64, x)
uint_unmap(::Type{Float64}, u::UInt64, ::Right) = reinterpret(Float64, u)
UIntMappable(::Type{Float64}, ::Union{Left, Right}) = UInt64

isnan(o::DirectOrdering, x::Floats) = (x!=x)
isnan(o::DirectOrdering, x::Missing) = false
Expand Down
Loading