|
| 1 | +import Base.@_inline_meta |
| 2 | +import Base.Order: Ordering, Forward, ReverseOrdering, ord |
| 3 | +import Base.Sort: Algorithm, defalg, lt, sort |
| 4 | + |
| 5 | + |
| 6 | +struct BitonicSortAlg <: Algorithm end |
| 7 | + |
| 8 | +const BitonicSort = BitonicSortAlg() |
| 9 | + |
| 10 | +# BitonicSort has non-optimal asymptotic behaviour, so we define a cutoff length. |
| 11 | +# This also prevents compilation time to skyrocket for larger vectors. |
| 12 | +defalg(a::StaticVector) = isimmutable(a) && length(a) <= 20 ? BitonicSort : QuickSort |
| 13 | + |
| 14 | +@inline function sort(a::StaticVector; |
| 15 | + alg::Algorithm = defalg(a), |
| 16 | + lt = isless, |
| 17 | + by = identity, |
| 18 | + rev::Union{Bool,Nothing} = nothing, |
| 19 | + order::Ordering = Forward) |
| 20 | + length(a) <= 1 && return a |
| 21 | + ordr = ord(lt, by, rev, order) |
| 22 | + return _sort(Size(a), alg, ordr, a) |
| 23 | +end |
| 24 | + |
| 25 | +@inline _sort(_, alg, order, a::StaticVector) = sort!(Base.copymutable(a); alg=alg, order=order) |
| 26 | +@inline _sort(_, alg::BitonicSortAlg, order, a::StaticVector) = similar_type(a)(_sort(Tuple(a), alg, order)) |
| 27 | + |
| 28 | +# Implementation loosely following |
| 29 | +# https://www.inf.hs-flensburg.de/lang/algorithmen/sortieren/bitonic/oddn.htm |
| 30 | +@generated function _sort(a::NTuple{N}, ::BitonicSortAlg, order) where N |
| 31 | + function swap_expr(i, j, rev) |
| 32 | + ai = Symbol('a', i) |
| 33 | + aj = Symbol('a', j) |
| 34 | + order = rev ? :revorder : :order |
| 35 | + return :( ($ai, $aj) = lt($order, $ai, $aj) ? ($ai, $aj) : ($aj, $ai) ) |
| 36 | + end |
| 37 | + |
| 38 | + function merge_exprs(idx, rev) |
| 39 | + exprs = Expr[] |
| 40 | + length(idx) == 1 && return exprs |
| 41 | + |
| 42 | + ci = 2^(ceil(Int, log2(length(idx))) - 1) |
| 43 | + # TODO: generate simd code for these swaps |
| 44 | + for i in first(idx):last(idx)-ci |
| 45 | + push!(exprs, swap_expr(i, i+ci, rev)) |
| 46 | + end |
| 47 | + append!(exprs, merge_exprs(idx[1:ci], rev)) |
| 48 | + append!(exprs, merge_exprs(idx[ci+1:end], rev)) |
| 49 | + return exprs |
| 50 | + end |
| 51 | + |
| 52 | + function sort_exprs(idx, rev=false) |
| 53 | + exprs = Expr[] |
| 54 | + length(idx) == 1 && return exprs |
| 55 | + |
| 56 | + append!(exprs, sort_exprs(idx[1:end÷2], !rev)) |
| 57 | + append!(exprs, sort_exprs(idx[end÷2+1:end], rev)) |
| 58 | + append!(exprs, merge_exprs(idx, rev)) |
| 59 | + return exprs |
| 60 | + end |
| 61 | + |
| 62 | + idx = 1:N |
| 63 | + symlist = (Symbol('a', i) for i in idx) |
| 64 | + return quote |
| 65 | + @_inline_meta |
| 66 | + revorder = Base.Order.ReverseOrdering(order) |
| 67 | + ($(symlist...),) = a |
| 68 | + ($(sort_exprs(idx)...);) |
| 69 | + return ($(symlist...),) |
| 70 | + end |
| 71 | +end |
0 commit comments