Skip to content

Commit

Permalink
Merge pull request #3952 from kmsquire/radix_sort
Browse files Browse the repository at this point in the history
RFC: RadixSort
  • Loading branch information
StefanKarpinski committed Aug 7, 2013
2 parents 528dd2c + 8704f08 commit 6fbac9a
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 20 deletions.
1 change: 1 addition & 0 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export
QR,
QRPivoted,
QuickSort,
RadixSort,
Range,
Range1,
RangeIndex,
Expand Down
56 changes: 45 additions & 11 deletions base/ordering.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ module Order
## notions of element ordering ##

export # not exported by Base
Ordering, Forward, By, Lt, Perm,
Ordering, Forward,
By, Lt, Perm,
ReverseOrdering, ForwardOrdering,
lt, ord
DirectOrdering,
lt, uint_mapping, ord, ordtype
# Reverse, # TODO: clashes with Reverse iterator

abstract Ordering
Expand All @@ -18,29 +20,61 @@ end
ReverseOrdering(rev::ReverseOrdering) = rev.fwd
ReverseOrdering{Fwd}(fwd::Fwd) = ReverseOrdering{Fwd}(fwd)

typealias DirectOrdering Union(ForwardOrdering,ReverseOrdering{ForwardOrdering})

const Forward = ForwardOrdering()
const Reverse = ReverseOrdering(Forward)

immutable By <: Ordering
by::Function
end

immutable Lt <: Ordering
lt::Function
end

const Forward = ForwardOrdering()
const Reverse = ReverseOrdering(Forward)

lt(o::ForwardOrdering, a, b) = isless(a,b)
lt(o::ReverseOrdering, a, b) = lt(o.fwd,b,a)
lt(o::By, a, b) = isless(o.by(a),o.by(b))
lt(o::Lt, a, b) = o.lt(a,b)

immutable Perm{O<:Ordering,V<:AbstractVector} <: Ordering
order::O
data::V
end
Perm{O<:Ordering,V<:AbstractVector}(o::O,v::V) = Perm{O,V}(o,v)

lt(o::ForwardOrdering, a, b) = isless(a,b)
lt(o::ReverseOrdering, a, b) = lt(o.fwd,b,a)
lt(o::By, a, b) = isless(o.by(a),o.by(b))
lt(o::Lt, a, b) = o.lt(a,b)
lt(p::Perm, a, b) = lt(p.order, p.data[a], p.data[b])

# Map a bits-type to an unsigned int, maintaining sort order
uint_mapping(::ForwardOrdering, x::Unsigned) = x
uint_mapping(::ForwardOrdering, x::Int8) = uint8 (x $ typemin(Int8))
uint_mapping(::ForwardOrdering, x::Int16) = uint16 (x $ typemin(Int16))
uint_mapping(::ForwardOrdering, x::Int32) = uint32 (x $ typemin(Int32))
uint_mapping(::ForwardOrdering, x::Int64) = uint64 (x $ typemin(Int64))
uint_mapping(::ForwardOrdering, x::Int128) = uint128(x $ typemin(Int128))
uint_mapping(::ForwardOrdering, x::Float32) = (y = reinterpret(Int32, x); uint32(y < 0 ? ~y : (y $ typemin(Int32))))
uint_mapping(::ForwardOrdering, x::Float64) = (y = reinterpret(Int64, x); uint64(y < 0 ? ~y : (y $ typemin(Int64))))

uint_mapping{Fwd}(::ReverseOrdering{Fwd}, x) = ~uint_mapping(Fwd, x)
#uint_mapping{T<:Real}(::ReverseOrdering{ForwardOrdering}, x::T) = ~uint_mapping(Forward, x) ## Manually inlined
uint_mapping(::ReverseOrdering{ForwardOrdering}, x::Unsigned) = ~x
uint_mapping(::ReverseOrdering{ForwardOrdering}, x::Int8) = ~uint8 (x $ typemin(Int8))
uint_mapping(::ReverseOrdering{ForwardOrdering}, x::Int16) = ~uint16 (x $ typemin(Int16))
uint_mapping(::ReverseOrdering{ForwardOrdering}, x::Int32) = ~uint32 (x $ typemin(Int32))
uint_mapping(::ReverseOrdering{ForwardOrdering}, x::Int64) = ~uint64 (x $ typemin(Int64))
uint_mapping(::ReverseOrdering{ForwardOrdering}, x::Int128) = ~uint128(x $ typemin(Int128))
uint_mapping(::ReverseOrdering{ForwardOrdering}, x::Float32) = (y = reinterpret(Int32, x); uint32(y < 0 ? y : ~(y $ typemin(Int32))))
uint_mapping(::ReverseOrdering{ForwardOrdering}, x::Float64) = (y = reinterpret(Int64, x); uint64(y < 0 ? y : ~(y $ typemin(Int64))))

uint_mapping(o::By, x) = uint_mapping(Forward, o.by(x))
uint_mapping(o::Perm, i::Int) = uint_mapping(o.order, o.data[i])

ordtype (o::ReverseOrdering, vs::AbstractArray) = ordtype(o.fwd, vs)
ordtype (o::Perm, vs::AbstractArray) = ordtype(o.order, o.data)
# TODO: here, we really want the return type of o.by, without calling it
ordtype (o::By, vs::AbstractArray) = try typeof(o.by(vs[1])) catch Any end

ordtype{T}(o::Ordering, vs::AbstractArray{T}) = T

function ord(lt::Function, by::Function, rev::Bool, order::Ordering=Forward)
o = (lt===isless) & (by===identity) ? order :
Expand All @@ -50,4 +84,4 @@ function ord(lt::Function, by::Function, rev::Bool, order::Ordering=Forward)
rev ? ReverseOrdering(o) : o
end

end
end
85 changes: 76 additions & 9 deletions base/sort.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ export # also exported by Base
QuickSort,
MergeSort,
TimSort,
HeapSort
HeapSort,
RadixSort

export # not exported by Base
Algorithm,
Expand Down Expand Up @@ -233,12 +234,14 @@ immutable QuickSortAlg <: Algorithm end
immutable HeapSortAlg <: Algorithm end
immutable MergeSortAlg <: Algorithm end
immutable TimSortAlg <: Algorithm end
immutable RadixSortAlg <: Algorithm end

const InsertionSort = InsertionSortAlg()
const QuickSort = QuickSortAlg()
const HeapSort = HeapSortAlg()
const MergeSort = MergeSortAlg()
const TimSort = TimSortAlg()
const RadixSort = RadixSortAlg()

const DEFAULT_UNSTABLE = QuickSort
const DEFAULT_STABLE = MergeSort
Expand Down Expand Up @@ -333,6 +336,68 @@ function sort!(v::AbstractVector, lo::Int, hi::Int, a::MergeSortAlg, o::Ordering
return v
end

const RADIX_SIZE = 11
const RADIX_MASK = 0x7FF

function sort!(vs::AbstractVector, lo::Int, hi::Int, ::RadixSortAlg, o::Ordering, ts=similar(vs))
# Input checking
if lo >= hi; return vs; end

# Make sure we're sorting a bits type
T = ordtype(o, vs)
if !isbits(T)
error("Radix sort only sorts bits types (got $T)")
end

# Init
iters = iceil(sizeof(T)*8/RADIX_SIZE)
bin = zeros(Uint32, 2^RADIX_SIZE, iters)
if lo > 1; bin[1,:] = lo-1; end

# Histogram for each element, radix
for i = lo:hi
v = uint_mapping(o, vs[i])
for j = 1:iters
idx = int((v >> (j-1)*RADIX_SIZE) & RADIX_MASK)+1
@inbounds bin[idx,j] += 1
end
end

# Sort!
swaps = 0
len = hi-lo+1
for j = 1:iters
# Unroll first data iteration, check for degenerate case
v = uint_mapping(o, vs[hi])
idx = int((v >> (j-1)*RADIX_SIZE) & RADIX_MASK)+1

# are all values the same at this radix?
if bin[idx,j] == len; continue; end

cbin = cumsum(bin[:,j])
ci = cbin[idx]
ts[ci] = vs[hi]
cbin[idx] -= 1

# Finish the loop...
@inbounds for i in hi-1:-1:lo
v = uint_mapping(o, vs[i])
idx = int((v >> (j-1)*RADIX_SIZE) & RADIX_MASK)+1
ci = cbin[idx]
ts[ci] = vs[i]
cbin[idx] -= 1
end
vs,ts = ts,vs
swaps += 1
end

if isodd(swaps)
vs,ts = ts,vs
copy!(vs,ts)
end
vs
end

include("timsort.jl")

## generic sorting methods ##
Expand Down Expand Up @@ -379,26 +444,28 @@ using ...Order

import Core.Intrinsics: unbox, slt_int
import ..Sort: sort!
import ...Order: lt
import ...Order: lt, DirectOrdering, uint_mapping

typealias Floats Union(Float32,Float64)
typealias Direct Union(ForwardOrdering,ReverseOrdering{ForwardOrdering})

immutable Left <: Ordering end
immutable Right <: Ordering end

left(::Direct) = Left()
right(::Direct) = Right()
left(::DirectOrdering) = Left()
right(::DirectOrdering) = Right()

left(o::Perm) = Perm(left(o.order),o.data)
right(o::Perm) = Perm(right(o.order),o.data)

lt{T<:Floats}(::Left, x::T, y::T) = slt_int(unbox(T,y),unbox(T,x))
lt{T<:Floats}(::Right, x::T, y::T) = slt_int(unbox(T,x),unbox(T,y))

isnan(o::Direct, x::Floats) = (x!=x)
isnan(o::DirectOrdering, x::Floats) = (x!=x)
isnan(o::Perm, i::Int) = isnan(o.order,o.data[i])

uint_mapping{F<:Floats}(::Right, x::F) = uint_mapping(Forward, x)
uint_mapping{F<:Floats}(::Left , x::F) = uint_mapping(Forward, x)

function nans2left!(v::AbstractVector, o::Ordering, lo::Int=1, hi::Int=length(v))
hi < lo && return lo, hi
i = lo
Expand Down Expand Up @@ -447,7 +514,7 @@ nans2end!(v::AbstractVector, o::ReverseOrdering) = nans2left!(v,o)
nans2end!{O<:ForwardOrdering}(v::AbstractVector{Int}, o::Perm{O}) = nans2right!(v,o)
nans2end!{O<:ReverseOrdering}(v::AbstractVector{Int}, o::Perm{O}) = nans2left!(v,o)

issignleft(o::Direct, x::Floats) = lt(o, x, zero(x))
issignleft(o::DirectOrdering, x::Floats) = lt(o, x, zero(x))
issignleft(o::Perm, i::Int) = issignleft(o.order, o.data[i])

function fpsort!(v::AbstractVector, a::Algorithm, o::Ordering)
Expand All @@ -468,8 +535,8 @@ function fpsort!(v::AbstractVector, a::Algorithm, o::Ordering)
return v
end

sort!{T<:Floats}(v::AbstractVector{T}, a::Algorithm, o::Direct) = fpsort!(v,a,o)
sort!{O<:Direct,T<:Floats}(v::Vector{Int}, a::Algorithm, o::Perm{O,Vector{T}}) = fpsort!(v,a,o)
sort!{T<:Floats}(v::AbstractVector{T}, a::Algorithm, o::DirectOrdering) = fpsort!(v,a,o)
sort!{O<:DirectOrdering,T<:Floats}(v::Vector{Int}, a::Algorithm, o::Perm{O,Vector{T}}) = fpsort!(v,a,o)

end # module Sort.Float

Expand Down

0 comments on commit 6fbac9a

Please sign in to comment.