diff --git a/src/SortingAlgorithms.jl b/src/SortingAlgorithms.jl
index b9b0c8e..64ed633 100644
--- a/src/SortingAlgorithms.jl
+++ b/src/SortingAlgorithms.jl
@@ -9,12 +9,13 @@ using Base.Order
import Base.Sort: sort!
import DataStructures: heapify!, percolate_down!
-export HeapSort, TimSort, RadixSort, CombSort
+export HeapSort, TimSort, RadixSort, CombSort, BitonicSort
struct HeapSortAlg <: Algorithm end
struct TimSortAlg <: Algorithm end
struct RadixSortAlg <: Algorithm end
struct CombSortAlg <: Algorithm end
+struct BitonicSortAlg <: Algorithm end
const HeapSort = HeapSortAlg()
const TimSort = TimSortAlg()
@@ -46,6 +47,25 @@ Characteristics:
"""
const CombSort = CombSortAlg()
+"""
+ BitonicSort
+
+Indicates that a sorting function should use the bitonic mergesort algorithm.
+This algorithm performs a series of pre-determined comparisons, and tends to be very parallelizable.
+The algorithm effectively implements a sorting network based on merging bitonic sequences.
+
+Characteristics:
+ - *not stable* does not preserve the ordering of elements which
+ compare equal (e.g. "a" and "A" in a sort of letters which
+ ignores case).
+ - *in-place* in memory.
+ - *parallelizable* suitable for vectorization with SIMD instructions because
+it performs many independent comparisons.
+
+See wikipedia.org/wiki/Bitonic_sorter for more information.
+"""
+const BitonicSort = BitonicSortAlg()
+
## Heap sort
@@ -609,9 +629,8 @@ function sort!(v::AbstractVector, lo::Int, hi::Int, ::CombSortAlg, o::Ordering)
interval = (3 * (hi-lo+1)) >> 2
while interval > 1
- @inbounds for j in lo:hi-interval
- a, b = v[j], v[j+interval]
- v[j], v[j+interval] = lt(o, b, a) ? (b, a) : (a, b)
+ for j in lo:hi-interval
+ @inbounds comparator!(v, j, j+interval, o)
end
interval = (3 * interval) >> 2
end
@@ -619,4 +638,53 @@ function sort!(v::AbstractVector, lo::Int, hi::Int, ::CombSortAlg, o::Ordering)
return sort!(v, lo, hi, InsertionSort, o)
end
+function sort!(v::AbstractVector, lo::Int, hi::Int, ::BitonicSortAlg, o::Ordering)
+ return bitonicsort!(view(v, lo:hi), o::Ordering)
+end
+
+function bitonicsort!(data, o::Ordering)
+ N = length(data)
+ for n in 1:intlog2(N)
+ bitonicfirststage!(data, Val(n), o::Ordering)
+ for m in n-1:-1:1
+ bitonicsecondstage!(data, Val(m), o::Ordering)
+ end
+ end
+ return data
+end
+
+function bitonicfirststage!(v, ::Val{Gap}, o::Ordering) where Gap
+ N = length(v)
+ gap = 1 << Gap
+ halfgap = 1 << (Gap - 1)
+ for i in 0:gap:N-1
+ firstj = max(0, i + gap - N)
+ for j in firstj:(halfgap-1)
+ ia = i + j
+ ib = i + gap - j - 1
+ @inbounds comparator!(v, ia + 1, ib + 1, o)
+ end
+ end
+end
+
+function bitonicsecondstage!(v, ::Val{Gap}, o::Ordering) where Gap
+ N = length(v)
+ gap = 1 << Gap
+ for i in 0:gap:N-1
+ lastj = min(N - 1, N - (i + gap >> 1 + 1))
+ for j in 0:lastj
+ ia = i + j
+ ib = i + j + gap >> 1
+ @inbounds comparator!(v, ia + 1, ib + 1, o)
+ end
+ end
+end
+
+intlog2(n) = (n > 1) ? 8sizeof(n-1)-leading_zeros(n-1) : 0
+
+Base.@propagate_inbounds function comparator!(v, i, j, o)
+ a, b = v[i], v[j]
+ v[i], v[j] = lt(o, b, a) ? (b, a) : (a, b)
+end
+
end # module
diff --git a/test/runtests.jl b/test/runtests.jl
index 7ea2780..0f4a558 100644
--- a/test/runtests.jl
+++ b/test/runtests.jl
@@ -5,7 +5,7 @@ using Random
a = rand(1:10000, 1000)
-for alg in [TimSort, HeapSort, RadixSort, CombSort]
+for alg in [TimSort, HeapSort, RadixSort, CombSort, BitonicSort]
b = sort(a, alg=alg)
@test issorted(b)
ix = sortperm(a, alg=alg)
@@ -85,7 +85,7 @@ for n in [0:10..., 100, 101, 1000, 1001]
end
# unstable algorithms
- for alg in [HeapSort, CombSort]
+ for alg in [HeapSort, CombSort, BitonicSort]
p = sortperm(v, alg=alg, order=ord)
@test isperm(p)
@test v[p] == si
@@ -99,7 +99,7 @@ for n in [0:10..., 100, 101, 1000, 1001]
v = randn_with_nans(n,0.1)
for ord in [Base.Order.Forward, Base.Order.Reverse],
- alg in [TimSort, HeapSort, RadixSort, CombSort]
+ alg in [TimSort, HeapSort, RadixSort, CombSort, BitonicSort]
# test float sorting with NaNs
s = sort(v, alg=alg, order=ord)
@test issorted(s, order=ord)