• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JuliaLang / julia / #38002

06 Feb 2025 06:14AM UTC coverage: 20.322% (-2.4%) from 22.722%
#38002

push

local

web-flow
bpart: Fully switch to partitioned semantics (#57253)

This is the final PR in the binding partitions series (modulo bugs and
tweaks), i.e. it closes #54654 and thus closes #40399, which was the
original design sketch.

This thus activates the full designed semantics for binding partitions,
in particular allowing safe replacement of const bindings. It in
particular allows struct redefinitions. This thus closes
timholy/Revise.jl#18 and also closes #38584.

The biggest semantic change here is probably that this gets rid of the
notion of "resolvedness" of a binding. Previously, a lot of the behavior
of our implementation depended on when bindings were "resolved", which
could happen at basically an arbitrary point (in the compiler, in REPL
completion, in a different thread), making a lot of the semantics around
bindings ill- or at least implementation-defined. There are several
related issues in the bugtracker, so this closes #14055 closes #44604
closes #46354 closes #30277

It is also the last step to close #24569.
It also supports bindings for undef->defined transitions and thus closes
#53958 closes #54733 - however, this is not activated yet for
performance reasons and may need some further optimization.

Since resolvedness no longer exists, we need to replace it with some
hopefully more well-defined semantics. I will describe the semantics
below, but before I do I will make two notes:

1. There are a number of cases where these semantics will behave
slightly differently than the old semantics absent some other task going
around resolving random bindings.
2. The new behavior (except for the replacement stuff) was generally
permissible under the old semantics if the bindings happened to be
resolved at the right time.

With all that said, there are essentially three "strengths" of bindings:

1. Implicit Bindings: Anything implicitly obtained from `using Mod`, "no
binding", plus slightly more exotic corner cases around conflicts

2. Weakly declared bindin... (continued)

11 of 111 new or added lines in 7 files covered. (9.91%)

1273 existing lines in 68 files now uncovered.

9908 of 48755 relevant lines covered (20.32%)

105126.48 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

17.05
/base/sort.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
module Sort
4

5
using Base.Order
6

7
using Base: copymutable, midpoint, require_one_based_indexing, uinttype, tail,
8
    sub_with_overflow, add_with_overflow, OneTo, BitSigned, BitIntegerType, top_set_bit
9

10
import Base:
11
    sort,
12
    sort!,
13
    issorted,
14
    sortperm,
15
    to_indices
16

17
export # also exported by Base
18
    # order-only:
19
    issorted,
20
    searchsorted,
21
    searchsortedfirst,
22
    searchsortedlast,
23
    insorted,
24
    # order & algorithm:
25
    sort,
26
    sort!,
27
    sortperm,
28
    sortperm!,
29
    partialsort,
30
    partialsort!,
31
    partialsortperm,
32
    partialsortperm!,
33
    # algorithms:
34
    InsertionSort,
35
    QuickSort,
36
    MergeSort,
37
    PartialQuickSort
38

39
export # not exported by Base
40
    Algorithm,
41
    DEFAULT_UNSTABLE,
42
    DEFAULT_STABLE,
43
    SMALL_ALGORITHM,
44
    SMALL_THRESHOLD
45

46
abstract type Algorithm end
47

48
## functions requiring only ordering ##
49

50
function issorted(itr, order::Ordering)
254✔
51
    y = iterate(itr)
254✔
52
    y === nothing && return true
254✔
53
    prev, state = y
160✔
54
    y = iterate(itr, state)
168✔
55
    while y !== nothing
3,176✔
56
        this, state = y
2,986✔
57
        lt(order, this, prev) && return false
6,021✔
58
        prev = this
2,986✔
59
        y = iterate(itr, state)
3,008✔
60
    end
3,008✔
61
    return true
163✔
62
end
63

64
"""
65
    issorted(v, lt=isless, by=identity, rev::Bool=false, order::Base.Order.Ordering=Base.Order.Forward)
66

67
Test whether a collection is in sorted order. The keywords modify what
68
order is considered sorted, as described in the [`sort!`](@ref) documentation.
69

70
# Examples
71
```jldoctest
72
julia> issorted([1, 2, 3])
73
true
74

75
julia> issorted([(1, "b"), (2, "a")], by = x -> x[1])
76
true
77

78
julia> issorted([(1, "b"), (2, "a")], by = x -> x[2])
79
false
80

81
julia> issorted([(1, "b"), (2, "a")], by = x -> x[2], rev=true)
82
true
83

84
julia> issorted([1, 2, -2, 3], by=abs)
85
true
86
```
87
"""
88
function issorted(itr;
246✔
89
        lt=isless, by=identity, rev::Union{Bool,Nothing}=nothing, order::Ordering=Forward)
90
    # Explicit branching because the compiler can't optimize away the
91
    # type instability of the `ord` call with Bool `rev` parameter.
92
    if rev === true
×
93
        issorted(itr, ord(lt, by, true, order))
×
94
    else
95
        issorted(itr, ord(lt, by, nothing, order))
246✔
96
    end
97
end
98

99
function partialsort!(v::AbstractVector, k::Union{Integer,OrdinalRange}, o::Ordering)
×
100
    # TODO move k from `alg` to `kw`
101
    # Don't perform InitialOptimizations before Bracketing. The optimizations take O(n)
102
    # time and so does the whole sort. But do perform them before recursive calls because
103
    # that can cause significant speedups when the target range is large so the runtime is
104
    # dominated by k log k and the optimizations runs in O(k) time.
105
    _sort!(v, BoolOptimization(
×
106
        Small{12}( # Very small inputs should go straight to insertion sort
107
            BracketedSort(k))),
108
        o, (;))
109
    maybeview(v, k)
×
110
end
111

112
maybeview(v, k) = view(v, k)
×
113
maybeview(v, k::Integer) = v[k]
×
114

115
"""
116
    partialsort!(v, k; by=identity, lt=isless, rev=false)
117

118
Partially sort the vector `v` in place so that the value at index `k` (or
119
range of adjacent values if `k` is a range) occurs
120
at the position where it would appear if the array were fully sorted. If `k` is a single
121
index, that value is returned; if `k` is a range, an array of values at those indices is
122
returned. Note that `partialsort!` may not fully sort the input array.
123

124
For the keyword arguments, see the documentation of [`sort!`](@ref).
125

126

127
# Examples
128
```jldoctest
129
julia> a = [1, 2, 4, 3, 4]
130
5-element Vector{Int64}:
131
 1
132
 2
133
 4
134
 3
135
 4
136

137
julia> partialsort!(a, 4)
138
4
139

140
julia> a
141
5-element Vector{Int64}:
142
 1
143
 2
144
 3
145
 4
146
 4
147

148
julia> a = [1, 2, 4, 3, 4]
149
5-element Vector{Int64}:
150
 1
151
 2
152
 4
153
 3
154
 4
155

156
julia> partialsort!(a, 4, rev=true)
157
2
158

159
julia> a
160
5-element Vector{Int64}:
161
 4
162
 4
163
 3
164
 2
165
 1
166
```
167
"""
168
partialsort!(v::AbstractVector, k::Union{Integer,OrdinalRange};
×
169
             lt=isless, by=identity, rev::Union{Bool,Nothing}=nothing, order::Ordering=Forward) =
170
    partialsort!(v, k, ord(lt,by,rev,order))
171

172
"""
173
    partialsort(v, k, by=identity, lt=isless, rev=false)
174

175
Variant of [`partialsort!`](@ref) that copies `v` before partially sorting it, thereby returning the
176
same thing as `partialsort!` but leaving `v` unmodified.
177
"""
178
partialsort(v::AbstractVector, k::Union{Integer,OrdinalRange}; kws...) =
×
179
    partialsort!(copymutable(v), k; kws...)
180

181
# reference on sorted binary search:
182
#   https://www.tbray.org/ongoing/When/200x/2003/03/22/Binary
183

184
# index of the first value of vector a that is greater than or equivalent to x;
185
# returns lastindex(v)+1 if x is greater than all values in v.
186
function searchsortedfirst(v::AbstractVector, x, lo::T, hi::T, o::Ordering)::keytype(v) where T<:Integer
187
    hi = hi + T(1)
×
188
    len = hi - lo
×
189
    while len != 0
×
190
        half_len = len >>> 0x01
×
191
        m = lo + half_len
×
192
        if lt(o, @inbounds(v[m]), x)
×
193
            lo = m + 1
×
194
            len -= half_len + 1
×
195
        else
196
            hi = m
×
197
            len = half_len
×
198
        end
199
    end
×
200
    return lo
×
201
end
202

203
# index of the last value of vector a that is less than or equivalent to x;
204
# returns firstindex(v)-1 if x is less than all values of v.
205
function searchsortedlast(v::AbstractVector, x, lo::T, hi::T, o::Ordering)::keytype(v) where T<:Integer
206
    u = T(1)
×
207
    lo = lo - u
×
208
    hi = hi + u
18✔
209
    while lo != hi - u
54✔
210
        m = midpoint(lo, hi)
36✔
211
        if lt(o, x, @inbounds(v[m]))
36✔
212
            hi = m
×
213
        else
214
            lo = m
×
215
        end
216
    end
36✔
217
    return lo
18✔
218
end
219

220
# returns the range of indices of v equivalent to x
221
# if v does not contain x, returns a 0-length range
222
# indicating the insertion point of x
223
function searchsorted(v::AbstractVector, x, ilo::T, ihi::T, o::Ordering)::UnitRange{keytype(v)} where T<:Integer
×
224
    u = T(1)
×
225
    lo = ilo - u
×
226
    hi = ihi + u
×
227
    while lo != hi - u
×
228
        m = midpoint(lo, hi)
×
229
        if lt(o, @inbounds(v[m]), x)
×
230
            lo = m
×
231
        elseif lt(o, x, @inbounds(v[m]))
×
232
            hi = m
×
233
        else
234
            a = searchsortedfirst(v, x, lo+u, m, o)
×
235
            b = searchsortedlast(v, x, m, hi-u, o)
×
236
            return a : b
×
237
        end
238
    end
×
239
    return (lo + 1) : (hi - 1)
×
240
end
241

242

243
const FastRangeOrderings = Union{DirectOrdering,Lt{typeof(<)},ReverseOrdering{Lt{typeof(<)}}}
244

245
function searchsortedlast(a::AbstractRange{<:Real}, x::Real, o::FastRangeOrderings)::keytype(a)
×
246
    require_one_based_indexing(a)
×
247
    f, h, l = first(a), step(a), last(a)
×
248
    if lt(o, x, f)
×
249
        0
×
250
    elseif h == 0 || !lt(o, x, l)
×
251
        length(a)
×
252
    else
253
        n = round(Integer, (x - f) / h + 1)
×
254
        lt(o, x, a[n]) ? n - 1 : n
×
255
    end
256
end
257

258
function searchsortedfirst(a::AbstractRange{<:Real}, x::Real, o::FastRangeOrderings)::keytype(a)
×
259
    require_one_based_indexing(a)
×
260
    f, h, l = first(a), step(a), last(a)
×
261
    if !lt(o, f, x)
×
262
        1
×
263
    elseif h == 0 || lt(o, l, x)
×
264
        length(a) + 1
×
265
    else
266
        n = round(Integer, (x - f) / h + 1)
×
267
        lt(o, a[n], x) ? n + 1 : n
×
268
    end
269
end
270

271
function searchsortedlast(a::AbstractRange{<:Integer}, x::Real, o::FastRangeOrderings)::keytype(a)
×
272
    require_one_based_indexing(a)
×
273
    f, h, l = first(a), step(a), last(a)
×
274
    if lt(o, x, f)
×
275
        0
×
276
    elseif h == 0 || !lt(o, x, l)
×
277
        length(a)
×
278
    else
279
        if !(o isa ReverseOrdering)
×
280
            fld(floor(Integer, x) - f, h) + 1
×
281
        else
282
            fld(ceil(Integer, x) - f, h) + 1
×
283
        end
284
    end
285
end
286

287
function searchsortedfirst(a::AbstractRange{<:Integer}, x::Real, o::FastRangeOrderings)::keytype(a)
×
288
    require_one_based_indexing(a)
×
289
    f, h, l = first(a), step(a), last(a)
×
290
    if !lt(o, f, x)
×
291
        1
×
292
    elseif h == 0 || lt(o, l, x)
×
293
        length(a) + 1
×
294
    else
295
        if !(o isa ReverseOrdering)
×
296
            cld(ceil(Integer, x) - f, h) + 1
×
297
        else
298
            cld(floor(Integer, x) - f, h) + 1
×
299
        end
300
    end
301
end
302

303
searchsorted(a::AbstractRange{<:Real}, x::Real, o::FastRangeOrderings) =
×
304
    searchsortedfirst(a, x, o) : searchsortedlast(a, x, o)
305

306
for s in [:searchsortedfirst, :searchsortedlast, :searchsorted]
307
    @eval begin
308
        $s(v::AbstractVector, x, o::Ordering) = $s(v,x,firstindex(v),lastindex(v),o)
18✔
309
        $s(v::AbstractVector, x;
×
310
           lt=isless, by=identity, rev::Union{Bool,Nothing}=nothing, order::Ordering=Forward) =
36✔
311
            $s(v,x,ord(lt,by,rev,order))
312
    end
313
end
314

315
"""
316
    searchsorted(v, x; by=identity, lt=isless, rev=false)
317

318
Return the range of indices in `v` where values are equivalent to `x`, or an
319
empty range located at the insertion point if `v` does not contain values
320
equivalent to `x`. The vector `v` must be sorted according to the order defined
321
by the keywords. Refer to [`sort!`](@ref) for the meaning of the keywords and
322
the definition of equivalence. Note that the `by` function is applied to the
323
searched value `x` as well as the values in `v`.
324

325
The range is generally found using binary search, but there are optimized
326
implementations for some inputs.
327

328
See also: [`searchsortedfirst`](@ref), [`sort!`](@ref), [`insorted`](@ref), [`findall`](@ref).
329

330
# Examples
331
```jldoctest
332
julia> searchsorted([1, 2, 4, 5, 5, 7], 4) # single match
333
3:3
334

335
julia> searchsorted([1, 2, 4, 5, 5, 7], 5) # multiple matches
336
4:5
337

338
julia> searchsorted([1, 2, 4, 5, 5, 7], 3) # no match, insert in the middle
339
3:2
340

341
julia> searchsorted([1, 2, 4, 5, 5, 7], 9) # no match, insert at end
342
7:6
343

344
julia> searchsorted([1, 2, 4, 5, 5, 7], 0) # no match, insert at start
345
1:0
346

347
julia> searchsorted([1=>"one", 2=>"two", 2=>"two", 4=>"four"], 2=>"two", by=first) # compare the keys of the pairs
348
2:3
349
```
350
""" searchsorted
351

352
"""
353
    searchsortedfirst(v, x; by=identity, lt=isless, rev=false)
354

355
Return the index of the first value in `v` that is not ordered before `x`.
356
If all values in `v` are ordered before `x`, return `lastindex(v) + 1`.
357

358
The vector `v` must be sorted according to the order defined by the keywords.
359
`insert!`ing `x` at the returned index will maintain the sorted order.
360
Refer to [`sort!`](@ref) for the meaning and use of the keywords.
361
Note that the `by` function is applied to the searched value `x` as well as the
362
values in `v`.
363

364
The index is generally found using binary search, but there are optimized
365
implementations for some inputs.
366

367
See also: [`searchsortedlast`](@ref), [`searchsorted`](@ref), [`findfirst`](@ref).
368

369
# Examples
370
```jldoctest
371
julia> searchsortedfirst([1, 2, 4, 5, 5, 7], 4) # single match
372
3
373

374
julia> searchsortedfirst([1, 2, 4, 5, 5, 7], 5) # multiple matches
375
4
376

377
julia> searchsortedfirst([1, 2, 4, 5, 5, 7], 3) # no match, insert in the middle
378
3
379

380
julia> searchsortedfirst([1, 2, 4, 5, 5, 7], 9) # no match, insert at end
381
7
382

383
julia> searchsortedfirst([1, 2, 4, 5, 5, 7], 0) # no match, insert at start
384
1
385

386
julia> searchsortedfirst([1=>"one", 2=>"two", 4=>"four"], 3=>"three", by=first) # compare the keys of the pairs
387
3
388
```
389
""" searchsortedfirst
390

391
"""
392
    searchsortedlast(v, x; by=identity, lt=isless, rev=false)
393

394
Return the index of the last value in `v` that is not ordered after `x`.
395
If all values in `v` are ordered after `x`, return `firstindex(v) - 1`.
396

397
The vector `v` must be sorted according to the order defined by the keywords.
398
`insert!`ing `x` immediately after the returned index will maintain the sorted order.
399
Refer to [`sort!`](@ref) for the meaning and use of the keywords.
400
Note that the `by` function is applied to the searched value `x` as well as the
401
values in `v`.
402

403
The index is generally found using binary search, but there are optimized
404
implementations for some inputs
405

406
# Examples
407
```jldoctest
408
julia> searchsortedlast([1, 2, 4, 5, 5, 7], 4) # single match
409
3
410

411
julia> searchsortedlast([1, 2, 4, 5, 5, 7], 5) # multiple matches
412
5
413

414
julia> searchsortedlast([1, 2, 4, 5, 5, 7], 3) # no match, insert in the middle
415
2
416

417
julia> searchsortedlast([1, 2, 4, 5, 5, 7], 9) # no match, insert at end
418
6
419

420
julia> searchsortedlast([1, 2, 4, 5, 5, 7], 0) # no match, insert at start
421
0
422

423
julia> searchsortedlast([1=>"one", 2=>"two", 4=>"four"], 3=>"three", by=first) # compare the keys of the pairs
424
2
425
```
426
""" searchsortedlast
427

428
"""
429
    insorted(x, v; by=identity, lt=isless, rev=false) -> Bool
430

431
Determine whether a vector `v` contains any value equivalent to `x`.
432
The vector `v` must be sorted according to the order defined by the keywords.
433
Refer to [`sort!`](@ref) for the meaning of the keywords and the definition of
434
equivalence. Note that the `by` function is applied to the searched value `x`
435
as well as the values in `v`.
436

437
The check is generally done using binary search, but there are optimized
438
implementations for some inputs.
439

440
See also [`in`](@ref).
441

442
# Examples
443
```jldoctest
444
julia> insorted(4, [1, 2, 4, 5, 5, 7]) # single match
445
true
446

447
julia> insorted(5, [1, 2, 4, 5, 5, 7]) # multiple matches
448
true
449

450
julia> insorted(3, [1, 2, 4, 5, 5, 7]) # no match
451
false
452

453
julia> insorted(9, [1, 2, 4, 5, 5, 7]) # no match
454
false
455

456
julia> insorted(0, [1, 2, 4, 5, 5, 7]) # no match
457
false
458

459
julia> insorted(2=>"TWO", [1=>"one", 2=>"two", 4=>"four"], by=first) # compare the keys of the pairs
460
true
461
```
462

463
!!! compat "Julia 1.6"
464
     `insorted` was added in Julia 1.6.
465
"""
466
function insorted end
467
insorted(x, v::AbstractVector; kw...) = !isempty(searchsorted(v, x; kw...))
×
468
insorted(x, r::AbstractRange) = in(x, r)
×
469

470
## Alternative keyword management
471

472
macro getkw(syms...)
473
    getters = (getproperty(Sort, Symbol(:_, sym)) for sym in syms)
474
    Expr(:block, (:($(esc(:((kw, $sym) = $getter(v, o, kw))))) for (sym, getter) in zip(syms, getters))...)
475
end
476

477
for (sym, exp, type) in [
478
        (:lo, :(firstindex(v)), Integer),
479
        (:hi, :(lastindex(v)),  Integer),
480
        (:mn, :(throw(ArgumentError("mn is needed but has not been computed"))), :(eltype(v))),
481
        (:mx, :(throw(ArgumentError("mx is needed but has not been computed"))), :(eltype(v))),
482
        (:scratch, nothing, :(Union{Nothing, Vector})), # could have different eltype
483
        (:legacy_dispatch_entry, nothing, Union{Nothing, Algorithm})]
484
    usym = Symbol(:_, sym)
485
    @eval function $usym(v, o, kw)
486
        # using missing instead of nothing because scratch could === nothing.
487
        res = get(kw, $(Expr(:quote, sym)), missing)
98,502✔
488
        res !== missing && return kw, res::$type
101,132✔
489
        $sym = $exp
26,310✔
490
        (;kw..., $sym), $sym::$type
26,310✔
491
    end
492
end
493

494
## Scratch space management
495

496
"""
497
    make_scratch(scratch::Union{Nothing, Vector}, T::Type, len::Integer)
498

499
Returns `(s, t)` where `t` is an `AbstractVector` of type `T` with length at least `len`
500
that is backed by the `Vector` `s`. If `scratch !== nothing`, then `s === scratch`.
501

502
This function will allocate a new vector if `scratch === nothing`, `resize!` `scratch` if it
503
is too short, and `reinterpret` `scratch` if its eltype is not `T`.
504
"""
505
function make_scratch(scratch::Nothing, T::Type, len::Integer)
506
    s = Vector{T}(undef, len)
150✔
507
    s, s
56✔
508
end
509
function make_scratch(scratch::Vector{T}, ::Type{T}, len::Integer) where T
×
510
    len > length(scratch) && resize!(scratch, len)
×
511
    scratch, scratch
×
512
end
513
function make_scratch(scratch::Vector, T::Type, len::Integer)
×
514
    len_bytes = len * sizeof(T)
×
515
    len_scratch = div(len_bytes, sizeof(eltype(scratch)))
×
516
    len_scratch > length(scratch) && resize!(scratch, len_scratch)
×
517
    scratch, reinterpret(T, scratch)
×
518
end
519

520

521
## sorting algorithm components ##
522

523
"""
524
    _sort!(v::AbstractVector, a::Base.Sort.Algorithm, o::Base.Order.Ordering, kw; t, offset)
525

526
An internal function that sorts `v` using the algorithm `a` under the ordering `o`,
527
subject to specifications provided in `kw` (such as `lo` and `hi` in which case it only
528
sorts `view(v, lo:hi)`)
529

530
Returns a scratch space if provided or constructed during the sort, or `nothing` if
531
no scratch space is present.
532

533
!!! note
534
    `_sort!` modifies but does not return `v`.
535

536
A returned scratch space will be a `Vector{T}` where `T` is usually the eltype of `v`. There
537
are some exceptions, for example if `eltype(v) == Union{Missing, T}` then the scratch space
538
may be a `Vector{T}` due to `MissingOptimization` changing the eltype of `v` to `T`.
539

540
`t` is an appropriate scratch space for the algorithm at hand, to be accessed as
541
`t[i + offset]`. `t` is used for an algorithm to pass a scratch space back to itself in
542
internal or recursive calls.
543
"""
544
function _sort! end
545

546
# TODO: delete this optimization when views have no overhead.
547
const UnwrappableSubArray = SubArray{T, 1, <:AbstractArray{T}, <:Tuple{AbstractUnitRange, Vararg{Number}}, true} where T
548
"""
549
    SubArrayOptimization(next) isa Base.Sort.Algorithm
550

551
Unwrap certain known SubArrays because views have a performance overhead 😢
552

553
Specifically, unwraps some instances of the type
554

555
    $UnwrappableSubArray
556
"""
557
struct SubArrayOptimization{T <: Algorithm} <: Algorithm
558
    next::T
559
end
560

561
_sort!(v::AbstractVector, a::SubArrayOptimization, o::Ordering, kw) = _sort!(v, a.next, o, kw)
14,479✔
562
function _sort!(v::UnwrappableSubArray, a::SubArrayOptimization, o::Ordering, kw)
×
563
    @getkw lo hi
×
564
    # @assert v.stride1 == 1
565
    parent = v.parent
×
566
    if parent isa Array && !(parent isa Vector) && hi - lo < 100
×
567
        # vec(::Array{T, ≠1}) allocates and is therefore somewhat expensive.
568
        # We don't want that for small inputs.
569
        _sort!(v, a.next, o, kw)
×
570
    else
571
        _sort!(vec(parent), a.next, o, (;kw..., lo = lo + v.offset1, hi = hi + v.offset1))
×
572
    end
573
end
574

575
"""
576
    MissingOptimization(next) isa Base.Sort.Algorithm
577

578
Filter out missing values.
579

580
Missing values are placed after other values according to `DirectOrdering`s. This pass puts
581
them there and passes on a view into the original vector that excludes the missing values.
582
This pass is triggered for both `sort([1, missing, 3])` and `sortperm([1, missing, 3])`.
583
"""
584
struct MissingOptimization{T <: Algorithm} <: Algorithm
585
    next::T
586
end
587

588
struct WithoutMissingVector{T, U} <: AbstractVector{T}
589
    data::U
590
    function WithoutMissingVector(data; unsafe=false)
×
591
        if !unsafe && any(ismissing, data)
×
592
            throw(ArgumentError("data must not contain missing values"))
×
593
        end
594
        new{nonmissingtype(eltype(data)), typeof(data)}(data)
×
595
    end
596
end
597
Base.@propagate_inbounds function Base.getindex(v::WithoutMissingVector, i::Integer)
×
598
    out = v.data[i]
×
599
    @assert !(out isa Missing)
×
600
    out::eltype(v)
×
601
end
602
Base.@propagate_inbounds function Base.setindex!(v::WithoutMissingVector, x, i::Integer)
×
603
    v.data[i] = x
×
604
    v
×
605
end
606
Base.size(v::WithoutMissingVector) = size(v.data)
×
607
Base.axes(v::WithoutMissingVector) = axes(v.data)
×
608

609
"""
610
    send_to_end!(f::Function, v::AbstractVector; [lo, hi])
611

612
Send every element of `v` for which `f` returns `true` to the end of the vector and return
613
the index of the last element for which `f` returns `false`.
614

615
`send_to_end!(f, v, lo, hi)` is equivalent to `send_to_end!(f, view(v, lo:hi))+lo-1`
616

617
Preserves the order of the elements that are not sent to the end.
618
"""
619
function send_to_end!(f::F, v::AbstractVector; lo=firstindex(v), hi=lastindex(v)) where F <: Function
×
620
    i = lo
×
621
    @inbounds while i <= hi && !f(v[i])
×
622
        i += 1
×
623
    end
×
624
    j = i + 1
×
625
    @inbounds while j <= hi
×
626
        if !f(v[j])
×
627
            v[i], v[j] = v[j], v[i]
×
628
            i += 1
×
629
        end
630
        j += 1
×
631
    end
×
632
    i - 1
×
633
end
634
"""
635
    send_to_end!(f::Function, v::AbstractVector, o::Base.Order.DirectOrdering[, end_stable]; lo, hi)
636

637
Return `(a, b)` where `v[a:b]` are the elements that are not sent to the end.
638

639
If `o isa ReverseOrdering` then the "end" of `v` is `v[lo]`.
640

641
If `end_stable` is set, the elements that are sent to the end are stable instead of the
642
elements that are not
643
"""
644
@inline send_to_end!(f::F, v::AbstractVector, ::ForwardOrdering, end_stable=false; lo, hi) where F <: Function =
×
645
    end_stable ? (lo, hi-send_to_end!(!f, view(v, hi:-1:lo))) : (lo, send_to_end!(f, v; lo, hi))
646
@inline send_to_end!(f::F, v::AbstractVector, ::ReverseOrdering, end_stable=false; lo, hi) where F <: Function =
×
647
    end_stable ? (send_to_end!(!f, v; lo, hi)+1, hi) : (hi-send_to_end!(f, view(v, hi:-1:lo))+1, hi)
648

649

650
function _sort!(v::AbstractVector, a::MissingOptimization, o::Ordering, kw)
651
    @getkw lo hi
14,384✔
652
    if o isa DirectOrdering && eltype(v) >: Missing && nonmissingtype(eltype(v)) != eltype(v)
11,926✔
653
        lo, hi = send_to_end!(ismissing, v, o; lo, hi)
×
654
        _sort!(WithoutMissingVector(v, unsafe=true), a.next, o, (;kw..., lo, hi))
×
655
    elseif o isa Perm && o.order isa DirectOrdering && eltype(v) <: Integer &&
11,926✔
656
                eltype(o.data) >: Missing && nonmissingtype(eltype(o.data)) != eltype(o.data) &&
657
                all(i === j for (i,j) in zip(v, eachindex(o.data)))
658
        # TODO make this branch known at compile time
659
        # This uses a custom function because we need to ensure stability of both sides and
660
        # we can assume v is equal to eachindex(o.data) which allows a copying partition
661
        # without allocations.
662
        lo_i, hi_i = lo, hi
×
663
        cv = eachindex(o.data) # equal to copy(v)
×
664
        for i in lo:hi
×
665
            x = o.data[cv[i]]
×
666
            if ismissing(x) == (o.order == Reverse) # should x go at the beginning/end?
×
667
                v[lo_i] = i
×
668
                lo_i += 1
×
669
            else
670
                v[hi_i] = i
×
671
                hi_i -= 1
×
672
            end
673
        end
×
674
        reverse!(v, lo_i, hi)
×
675
        if o.order == Reverse
×
676
            lo = lo_i
×
677
        else
678
            hi = hi_i
×
679
        end
680

681
        _sort!(v, a.next, Perm(o.order, WithoutMissingVector(o.data, unsafe=true)), (;kw..., lo, hi))
×
682
    else
683
        _sort!(v, a.next, o, kw)
14,479✔
684
    end
685
end
686

687

688
"""
689
    IEEEFloatOptimization(next) isa Base.Sort.Algorithm
690

691
Move NaN values to the end, partition by sign, and reinterpret the rest as unsigned integers.
692

693
IEEE floating point numbers (`Float64`, `Float32`, and `Float16`) compare the same as
694
unsigned integers with the bits with a few exceptions. This pass
695

696
This pass is triggered for both `sort([1.0, NaN, 3.0])` and `sortperm([1.0, NaN, 3.0])`.
697
"""
698
struct IEEEFloatOptimization{T <: Algorithm} <: Algorithm
699
    next::T
700
end
701

702
after_zero(::ForwardOrdering, x) = !signbit(x)
×
703
after_zero(::ReverseOrdering, x) = signbit(x)
×
704
is_concrete_IEEEFloat(T::Type) = T <: Base.IEEEFloat && isconcretetype(T)
55✔
705
function _sort!(v::AbstractVector, a::IEEEFloatOptimization, o::Ordering, kw)
706
    @getkw lo hi
366✔
707
    if is_concrete_IEEEFloat(eltype(v)) && o isa DirectOrdering
55✔
708
        lo, hi = send_to_end!(isnan, v, o, true; lo, hi)
×
709
        iv = reinterpret(uinttype(eltype(v)), v)
×
710
        j = send_to_end!(x -> after_zero(o, x), v; lo, hi)
×
711
        scratch = _sort!(iv, a.next, Reverse, (;kw..., lo, hi=j))
×
712
        if scratch === nothing # Union split
×
713
            _sort!(iv, a.next, Forward, (;kw..., lo=j+1, hi, scratch))
×
714
        else
715
            _sort!(iv, a.next, Forward, (;kw..., lo=j+1, hi, scratch))
×
716
        end
717
    elseif eltype(v) <: Integer && o isa Perm && o.order isa DirectOrdering && is_concrete_IEEEFloat(eltype(o.data))
55✔
718
        lo, hi = send_to_end!(i -> isnan(@inbounds o.data[i]), v, o.order, true; lo, hi)
×
719
        ip = reinterpret(uinttype(eltype(o.data)), o.data)
×
720
        j = send_to_end!(i -> after_zero(o.order, @inbounds o.data[i]), v; lo, hi)
×
721
        scratch = _sort!(v, a.next, Perm(Reverse, ip), (;kw..., lo, hi=j))
×
722
        if scratch === nothing # Union split
×
723
            _sort!(v, a.next, Perm(Forward, ip), (;kw..., lo=j+1, hi, scratch))
×
724
        else
725
            _sort!(v, a.next, Perm(Forward, ip), (;kw..., lo=j+1, hi, scratch))
×
726
        end
727
    else
728
        _sort!(v, a.next, o, kw)
366✔
729
    end
730
end
731

732

733
"""
734
    BoolOptimization(next) isa Base.Sort.Algorithm
735

736
Sort `AbstractVector{Bool}`s using a specialized version of counting sort.
737

738
Accesses each element at most twice (one read and one write), and performs at most two
739
comparisons.
740
"""
741
struct BoolOptimization{T <: Algorithm} <: Algorithm
742
    next::T
743
end
744
_sort!(v::AbstractVector, a::BoolOptimization, o::Ordering, kw) = _sort!(v, a.next, o, kw)
14,479✔
745
function _sort!(v::AbstractVector{Bool}, ::BoolOptimization, o::Ordering, kw)
×
746
    first = lt(o, false, true) ? false : lt(o, true, false) ? true : return v
×
747
    @getkw lo hi scratch
×
748
    count = 0
×
749
    @inbounds for i in lo:hi
×
750
        if v[i] == first
×
751
            count += 1
×
752
        end
753
    end
×
754
    @inbounds v[lo:lo+count-1] .= first
×
755
    @inbounds v[lo+count:hi] .= !first
×
756
    scratch
×
757
end
758

759

760
"""
761
    IsUIntMappable(yes, no) isa Base.Sort.Algorithm
762

763
Determines if the elements of a vector can be mapped to unsigned integers while preserving
764
their order under the specified ordering.
765

766
If they can be, dispatch to the `yes` algorithm and record the unsigned integer type that
767
the elements may be mapped to. Otherwise dispatch to the `no` algorithm.
768
"""
769
struct IsUIntMappable{T <: Algorithm, U <: Algorithm} <: Algorithm
770
    yes::T
771
    no::U
772
end
773
function _sort!(v::AbstractVector, a::IsUIntMappable, o::Ordering, kw)
774
    if UIntMappable(eltype(v), o) !== nothing
55✔
775
        _sort!(v, a.yes, o, kw)
×
776
    else
777
        _sort!(v, a.no, o, kw)
366✔
778
    end
779
end
780

781

782
"""
783
    Small{N}(small=SMALL_ALGORITHM, big) isa Base.Sort.Algorithm
784

785
Sort inputs with `length(lo:hi) <= N` using the `small` algorithm. Otherwise use the `big`
786
algorithm.
787
"""
788
struct Small{N, T <: Algorithm, U <: Algorithm} <: Algorithm
789
    small::T
790
    big::U
791
end
792
Small{N}(small, big) where N = Small{N, typeof(small), typeof(big)}(small, big)
×
793
Small{N}(big) where N = Small{N}(SMALL_ALGORITHM, big)
×
794
function _sort!(v::AbstractVector, a::Small{N}, o::Ordering, kw) where N
13,380✔
795
    @getkw lo hi
14,384✔
796
    if (hi-lo) < N
14,384✔
797
        _sort!(v, a.small, o, kw)
14,018✔
798
    else
799
        _sort!(v, a.big, o, kw)
366✔
800
    end
801
end
802

803

804
struct InsertionSortAlg <: Algorithm end
805

806
"""
807
    InsertionSort
808

809
Use the insertion sort algorithm.
810

811
Insertion sort traverses the collection one element at a time, inserting
812
each element into its correct, sorted position in the output vector.
813

814
Characteristics:
815
* *stable*: preserves the ordering of elements that compare equal
816
(e.g. "a" and "A" in a sort of letters that ignores case).
817
* *in-place* in memory.
818
* *quadratic performance* in the number of elements to be sorted:
819
it is well-suited to small collections but should not be used for large ones.
820
"""
821
const InsertionSort = InsertionSortAlg()
822

823
"""
824
    SMALL_ALGORITHM
825

826
Default sorting algorithm for small arrays.
827

828
This is an alias for a simple low-overhead algorithm that does not scale well
829
to large arrays, unlike high-overhead recursive algorithms used for larger arrays.
830
`SMALL_ALGORITHM` is a good choice for the base case of a recursive algorithm.
831
"""
832
const SMALL_ALGORITHM = InsertionSortAlg()
833

834
function _sort!(v::AbstractVector, ::InsertionSortAlg, o::Ordering, kw)
15,064✔
835
    @getkw lo hi scratch
15,064✔
836
    lo_plus_1 = (lo + 1)::Integer
15,064✔
837
    @inbounds for i = lo_plus_1:hi
16,387✔
838
        j = i
51,826✔
839
        x = v[i]
51,826✔
840
        while j > lo
135,814✔
841
            y = v[j-1]
109,765✔
842
            if !(lt(o, x, y)::Bool)
142,223✔
843
                break
25,777✔
844
            end
845
            v[j] = y
83,988✔
846
            j -= 1
83,988✔
847
        end
83,988✔
848
        v[j] = x
51,826✔
849
    end
89,911✔
850
    scratch
15,063✔
851
end
852

853

854
"""
855
    CheckSorted(next) isa Base.Sort.Algorithm
856

857
Check if the input is already sorted and for large inputs, also check if it is
858
reverse-sorted. The reverse-sorted check is unstable.
859
"""
860
struct CheckSorted{T <: Algorithm} <: Algorithm
861
    next::T
862
end
863
function _sort!(v::AbstractVector, a::CheckSorted, o::Ordering, kw)
×
864
    @getkw lo hi scratch
×
865

866
    # For most arrays, a presorted check is cheap (overhead < 5%) and for most large
867
    # arrays it is essentially free (<1%).
868
    _issorted(v, lo, hi, o) && return scratch
×
869

870
    # For most large arrays, a reverse-sorted check is essentially free (overhead < 1%)
871
    if hi-lo >= 500 && _issorted(v, lo, hi, ReverseOrdering(o))
×
872
        # If reversing is valid, do so. This violates stability.
873
        reverse!(v, lo, hi)
×
874
        return scratch
×
875
    end
876

877
    _sort!(v, a.next, o, kw)
×
878
end
879

880

881
"""
882
    ComputeExtrema(next) isa Base.Sort.Algorithm
883

884
Compute the extrema of the input under the provided order.
885

886
If the minimum is no less than the maximum, then the input is already sorted. Otherwise,
887
dispatch to the `next` algorithm.
888
"""
889
struct ComputeExtrema{T <: Algorithm} <: Algorithm
890
    next::T
891
end
892
function _sort!(v::AbstractVector, a::ComputeExtrema, o::Ordering, kw)
×
893
    @getkw lo hi scratch
×
894
    mn = mx = v[lo]
×
895
    @inbounds for i in (lo+1):hi
×
896
        vi = v[i]
×
897
        lt(o, vi, mn) && (mn = vi)
×
898
        lt(o, mx, vi) && (mx = vi)
×
899
    end
×
900

901
    lt(o, mn, mx) || return scratch # all same
×
902

903
    _sort!(v, a.next, o, (;kw..., mn, mx))
×
904
end
905

906

907
"""
908
    ConsiderCountingSort(counting=CountingSort(), next) isa Base.Sort.Algorithm
909

910
If the input's range is small enough, use the `counting` algorithm. Otherwise, dispatch to
911
the `next` algorithm.
912

913
For most types, the threshold is if the range is shorter than half the length, but for types
914
larger than Int64, bitshifts are expensive and RadixSort is not viable, so the threshold is
915
much more generous.
916
"""
917
struct ConsiderCountingSort{T <: Algorithm, U <: Algorithm} <: Algorithm
918
    counting::T
919
    next::U
920
end
921
ConsiderCountingSort(next) = ConsiderCountingSort(CountingSort(), next)
×
922
function _sort!(v::AbstractVector{<:Integer}, a::ConsiderCountingSort, o::DirectOrdering, kw)
×
923
    @getkw lo hi mn mx
×
924
    range = maybe_unsigned(o === Reverse ? mn-mx : mx-mn)
×
925

926
    if range < (sizeof(eltype(v)) > 8 ? 5(hi-lo)-100 : div(hi-lo, 2))
×
927
        _sort!(v, a.counting, o, kw)
×
928
    else
929
        _sort!(v, a.next, o, kw)
×
930
    end
931
end
932
_sort!(v::AbstractVector, a::ConsiderCountingSort, o::Ordering, kw) = _sort!(v, a.next, o, kw)
×
933

934

935
"""
936
    CountingSort() isa Base.Sort.Algorithm
937

938
Use the counting sort algorithm.
939

940
`CountingSort` is an algorithm for sorting integers that runs in Θ(length + range) time and
941
space. It counts the number of occurrences of each value in the input and then iterates
942
through those counts repopulating the input with the values in sorted order.
943
"""
944
struct CountingSort <: Algorithm end
945
maybe_reverse(o::ForwardOrdering, x) = x
×
946
maybe_reverse(o::ReverseOrdering, x) = reverse(x)
×
947
function _sort!(v::AbstractVector{<:Integer}, ::CountingSort, o::DirectOrdering, kw)
×
948
    @getkw lo hi mn mx scratch
×
949
    range = maybe_unsigned(o === Reverse ? mn-mx : mx-mn)
×
950
    offs = 1 - (o === Reverse ? mx : mn)
×
951

952
    counts = fill(0, range+1) # TODO use scratch (but be aware of type stability)
×
953
    @inbounds for i = lo:hi
×
954
        counts[v[i] + offs] += 1
×
955
    end
×
956

957
    idx = lo
×
958
    @inbounds for i = maybe_reverse(o, 1:range+1)
×
959
        lastidx = idx + counts[i] - 1
×
960
        val = i-offs
×
961
        for j = idx:lastidx
×
962
            v[j] = val isa Unsigned && eltype(v) <: Signed ? signed(val) : val
×
963
        end
×
964
        idx = lastidx + 1
×
965
    end
×
966

967
    scratch
×
968
end
969

970

971
"""
972
    ConsiderRadixSort(radix=RadixSort(), next) isa Base.Sort.Algorithm
973

974
If the number of bits in the input's range is small enough and the input supports efficient
975
bitshifts, use the `radix` algorithm. Otherwise, dispatch to the `next` algorithm.
976
"""
977
struct ConsiderRadixSort{T <: Algorithm, U <: Algorithm} <: Algorithm
978
    radix::T
979
    next::U
980
end
981
ConsiderRadixSort(next) = ConsiderRadixSort(RadixSort(), next)
×
982
function _sort!(v::AbstractVector, a::ConsiderRadixSort, o::DirectOrdering, kw)
×
983
    @getkw lo hi mn mx
×
984
    urange = uint_map(mx, o)-uint_map(mn, o)
×
985
    bits = unsigned(top_set_bit(urange))
×
986
    if sizeof(eltype(v)) <= 8 && bits+70 < 22log(hi-lo)
×
987
        _sort!(v, a.radix, o, kw)
×
988
    else
989
        _sort!(v, a.next, o, kw)
×
990
    end
991
end
992

993

994
"""
995
    RadixSort() isa Base.Sort.Algorithm
996

997
Use the radix sort algorithm.
998

999
`RadixSort` is a stable least significant bit first radix sort algorithm that runs in
1000
`O(length * log(range))` time and linear space.
1001

1002
It first sorts the entire vector by the last `chunk_size` bits, then by the second
1003
to last `chunk_size` bits, and so on. Stability means that it will not reorder two elements
1004
that compare equal. This is essential so that the order introduced by earlier,
1005
less significant passes is preserved by later passes.
1006

1007
Each pass divides the input into `2^chunk_size == mask+1` buckets. To do this, it
1008
 * counts the number of entries that fall into each bucket
1009
 * uses those counts to compute the indices to move elements of those buckets into
1010
 * moves elements into the computed indices in the swap array
1011
 * switches the swap and working array
1012

1013
`chunk_size` is larger for larger inputs and determined by an empirical heuristic.
1014
"""
1015
struct RadixSort <: Algorithm end
1016
function _sort!(v::AbstractVector, a::RadixSort, o::DirectOrdering, kw)
×
1017
    @getkw lo hi mn mx scratch
×
1018
    umn = uint_map(mn, o)
×
1019
    urange = uint_map(mx, o)-umn
×
1020
    bits = unsigned(top_set_bit(urange))
×
1021

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

1025
    # we subtract umn to avoid radixing over unnecessary bits. For example,
1026
    # Int32[3, -1, 2] uint_maps to UInt32[0x80000003, 0x7fffffff, 0x80000002]
1027
    # which uses all 32 bits, but once we subtract umn = 0x7fffffff, we are left with
1028
    # UInt32[0x00000004, 0x00000000, 0x00000003] which uses only 3 bits, and
1029
    # Float32[2.012, 400.0, 12.345] uint_maps to UInt32[0x3fff3b63, 0x3c37ffff, 0x414570a4]
1030
    # which is reduced to UInt32[0x03c73b64, 0x00000000, 0x050d70a5] using only 26 bits.
1031
    # the overhead for this subtraction is small enough that it is worthwhile in many cases.
1032

1033
    # this is faster than u[lo:hi] .-= umn as of v1.9.0-DEV.100
1034
    @inbounds for i in lo:hi
×
1035
        u[i] -= umn
×
1036
    end
×
1037

1038
    scratch, t = make_scratch(scratch, eltype(v), hi-lo+1)
×
1039
    tu = reinterpret(eltype(u), t)
×
1040
    if radix_sort!(u, lo, hi, bits, tu, 1-lo)
×
1041
        uint_unmap!(v, u, lo, hi, o, umn)
×
1042
    else
1043
        uint_unmap!(v, tu, lo, hi, o, umn, 1-lo)
×
1044
    end
1045
    scratch
×
1046
end
1047

1048

1049
"""
1050
    ScratchQuickSort(next::Base.Sort.Algorithm=Base.Sort.SMALL_ALGORITHM) isa Base.Sort.Algorithm
1051
    ScratchQuickSort(lo::Union{Integer, Missing}, hi::Union{Integer, Missing}=lo, next::Base.Sort.Algorithm=Base.Sort.SMALL_ALGORITHM) isa Base.Sort.Algorithm
1052

1053
Use the `ScratchQuickSort` algorithm with the `next` algorithm as a base case.
1054

1055
`ScratchQuickSort` is like `QuickSort`, but utilizes scratch space to operate faster and allow
1056
for the possibility of maintaining stability.
1057

1058
If `lo` and `hi` are provided, finds and sorts the elements in the range `lo:hi`, reordering
1059
but not necessarily sorting other elements in the process. If `lo` or `hi` is `missing`, it
1060
is treated as the first or last index of the input, respectively.
1061

1062
`lo` and `hi` may be specified together as an `AbstractUnitRange`.
1063

1064
Characteristics:
1065
  * *stable*: preserves the ordering of elements that compare equal
1066
    (e.g. "a" and "A" in a sort of letters that ignores case).
1067
  * *not in-place* in memory.
1068
  * *divide-and-conquer*: sort strategy similar to [`QuickSort`](@ref).
1069
  * *linear runtime* if `length(lo:hi)` is constant
1070
  * *quadratic worst case runtime* in pathological cases
1071
  (vanishingly rare for non-malicious input)
1072
"""
1073
struct ScratchQuickSort{L<:Union{Integer,Missing}, H<:Union{Integer,Missing}, T<:Algorithm} <: Algorithm
1074
    lo::L
1075
    hi::H
1076
    next::T
1077
end
1078
ScratchQuickSort(next::Algorithm=SMALL_ALGORITHM) = ScratchQuickSort(missing, missing, next)
×
1079
ScratchQuickSort(lo::Union{Integer, Missing}, hi::Union{Integer, Missing}) = ScratchQuickSort(lo, hi, SMALL_ALGORITHM)
×
1080
ScratchQuickSort(lo::Union{Integer, Missing}, next::Algorithm=SMALL_ALGORITHM) = ScratchQuickSort(lo, lo, next)
×
1081
ScratchQuickSort(r::OrdinalRange, next::Algorithm=SMALL_ALGORITHM) = ScratchQuickSort(first(r), last(r), next)
×
1082

1083
# select a pivot, partition v[lo:hi] according
1084
# to the pivot, and store the result in t[lo:hi].
1085
#
1086
# sets `pivot_dest[pivot_index+pivot_index_offset] = pivot` and returns that index.
1087
function partition!(t::AbstractVector, lo::Integer, hi::Integer, offset::Integer, o::Ordering,
1,138✔
1088
        v::AbstractVector, rev::Bool, pivot_dest::AbstractVector, pivot_index_offset::Integer)
1089
    # Ideally we would use `pivot_index = rand(lo:hi)`, but that requires Random.jl
1090
    # and would mutate the global RNG in sorting.
1091
    pivot_index = mod(hash(lo), lo:hi)
1,138✔
1092
    @inbounds begin
1,138✔
1093
        pivot = v[pivot_index]
1,138✔
1094
        while lo < pivot_index
42,429✔
1095
            x = v[lo]
41,291✔
1096
            fx = rev ? !lt(o, x, pivot) : lt(o, pivot, x)
50,477✔
1097
            t[(fx ? hi : lo) - offset] = x
41,291✔
1098
            offset += fx
41,291✔
1099
            lo += 1
41,291✔
1100
        end
41,291✔
1101
        while lo < hi
44,214✔
1102
            x = v[lo+1]
43,076✔
1103
            fx = rev ? lt(o, pivot, x) : !lt(o, x, pivot)
52,966✔
1104
            t[(fx ? hi : lo) - offset] = x
43,076✔
1105
            offset += fx
43,076✔
1106
            lo += 1
43,076✔
1107
        end
43,076✔
1108
        pivot_index = lo-offset + pivot_index_offset
1,138✔
1109
        pivot_dest[pivot_index] = pivot
1,138✔
1110
    end
1111

1112
    # t_pivot_index = lo-offset (i.e. without pivot_index_offset)
1113
    # t[t_pivot_index] is whatever it was before unless t is the pivot_dest
1114
    # t[<t_pivot_index] <* pivot, stable
1115
    # t[>t_pivot_index] >* pivot, reverse stable
1116

1117
    pivot_index
1,138✔
1118
end
1119

1120
function _sort!(v::AbstractVector, a::ScratchQuickSort, o::Ordering, kw;
2,576✔
1121
                t=nothing, offset=nothing, swap=false, rev=false)
1122
    @getkw lo hi scratch
1,288✔
1123

1124
    if t === nothing
1,288✔
1125
        scratch, t = make_scratch(scratch, eltype(v), hi-lo+1)
150✔
1126
        offset = 1-lo
150✔
1127
        kw = (;kw..., scratch)
150✔
1128
    end
1129

1130
    while lo < hi && hi - lo > SMALL_THRESHOLD
2,426✔
1131
        j = if swap
1,138✔
1132
            partition!(v, lo+offset, hi+offset, offset, o, t, rev, v, 0)
549✔
1133
        else
1134
            partition!(t, lo, hi, -offset, o, v, rev, v, -offset)
1,727✔
1135
        end
1136
        swap = !swap
1,138✔
1137

1138
        # For ScratchQuickSort(), a.lo === a.hi === missing, so the first two branches get skipped
1139
        if !ismissing(a.lo) && j <= a.lo # Skip sorting the lower part
1,138✔
1140
            swap && copyto!(v, lo, t, lo+offset, j-lo)
×
1141
            rev && reverse!(v, lo, j-1)
×
1142
            lo = j+1
×
1143
            rev = !rev
×
1144
        elseif !ismissing(a.hi) && a.hi <= j # Skip sorting the upper part
1,138✔
1145
            swap && copyto!(v, j+1, t, j+1+offset, hi-j)
×
1146
            rev || reverse!(v, j+1, hi)
×
1147
            hi = j-1
×
1148
        elseif j-lo < hi-j
1,138✔
1149
            # Sort the lower part recursively because it is smaller. Recursing on the
1150
            # smaller part guarantees O(log(n)) stack space even on pathological inputs.
1151
            _sort!(v, a, o, (;kw..., lo, hi=j-1); t, offset, swap, rev)
609✔
1152
            lo = j+1
609✔
1153
            rev = !rev
609✔
1154
        else # Sort the higher part recursively
1155
            _sort!(v, a, o, (;kw..., lo=j+1, hi); t, offset, swap, rev=!rev)
529✔
1156
            hi = j-1
529✔
1157
        end
1158
    end
1,138✔
1159
    hi < lo && return scratch
1,288✔
1160
    swap && copyto!(v, lo, t, lo+offset, hi-lo+1)
1,839✔
1161
    rev && reverse!(v, lo, hi)
1,237✔
1162
    _sort!(v, a.next, o, (;kw..., lo, hi))
1,237✔
1163
end
1164

1165

1166
"""
1167
    BracketedSort(target[, next::Algorithm]) isa Base.Sort.Algorithm
1168

1169
Perform a partialsort for the elements that fall into the indices specified by the `target`
1170
using BracketedSort with the `next` algorithm for subproblems.
1171

1172
BracketedSort takes a random* sample of the input, estimates the quantiles of the input
1173
using the quantiles of the sample to find signposts that almost certainly bracket the target
1174
values, filters the value in the input that fall between the signpost values to the front of
1175
the input, and then, if that "almost certainly" turned out to be true, finds the target
1176
within the small chunk that are, by value, between the signposts and now by position, at the
1177
front of the vector. On small inputs or when target is close to the size of the input,
1178
BracketedSort falls back to the `next` algorithm directly. Otherwise, BracketedSort uses the
1179
`next` algorithm only to compute quantiles of the sample and to find the target within the
1180
small chunk.
1181

1182
## Performance
1183

1184
If the `next` algorithm has `O(n * log(n))` runtime and the input is not pathological then
1185
the runtime of this algorithm is `O(n + k * log(k))` where `n` is the length of the input
1186
and `k` is `length(target)`. On pathological inputs the asymptotic runtime is the same as
1187
the runtime of the `next` algorithm.
1188

1189
BracketedSort itself does not allocate. If `next` is in-place then BracketedSort is also
1190
in-place. If `next` is not in place, and it's space usage increases monotonically with input
1191
length then BracketedSort's maximum space usage will never be more than the space usage
1192
of `next` on the input BracketedSort receives. For large nonpathological inputs and targets
1193
substantially smaller than the size of the input, BracketedSort's maximum memory usage will
1194
be much less than `next`'s. If the maximum additional space usage of `next` scales linearly
1195
then for small k the average* maximum additional space usage of BracketedSort will be
1196
`O(n^(2.3/3))`.
1197

1198
By default, BracketedSort uses the `O(n)` space and `O(n + k log k)` runtime
1199
`ScratchQuickSort` algorithm recursively.
1200

1201
*Sorting is unable to depend on Random.jl because Random.jl depends on sorting.
1202
 Consequently, we use `hash` as a source of randomness. The average runtime guarantees
1203
 assume that `hash(x::Int)` produces a random result. However, as this randomization is
1204
 deterministic, if you try hard enough you can find inputs that consistently reach the
1205
 worst case bounds. Actually constructing such inputs is an exercise left to the reader.
1206
 Have fun :).
1207

1208
Characteristics:
1209
  * *unstable*: does not preserve the ordering of elements that compare equal
1210
    (e.g. "a" and "A" in a sort of letters that ignores case).
1211
  * *in-place* in memory if the `next` algorithm is in-place.
1212
  * *estimate-and-filter*: strategy
1213
  * *linear runtime* if `length(target)` is constant and `next` is reasonable
1214
  * *n + k log k* worst case runtime if `next` has that runtime.
1215
  * *pathological inputs* can significantly increase constant factors.
1216
"""
1217
struct BracketedSort{T, F} <: Algorithm
1218
    target::T
1219
    get_next::F
1220
end
1221

1222
# TODO: this composition between BracketedSort and ScratchQuickSort does not bring me joy
1223
BracketedSort(k) = BracketedSort(k, k -> InitialOptimizations(ScratchQuickSort(k)))
×
1224

1225
function bracket_kernel!(v::AbstractVector, lo, hi, lo_signpost, hi_signpost, o)
×
1226
    i = 0
×
1227
    count_below = 0
×
1228
    checkbounds(v, lo:hi)
×
1229
    for j in lo:hi
×
1230
        x = @inbounds v[j]
×
1231
        a = lo_signpost !== nothing && lt(o, x, lo_signpost)
×
1232
        b = hi_signpost === nothing || !lt(o, hi_signpost, x)
×
1233
        count_below += a
×
1234
        # if a != b # This branch is almost never taken, so making it branchless is bad.
1235
        #     @inbounds v[i], v[j] = v[j], v[i]
1236
        #     i += 1
1237
        # end
1238
        c = a != b # JK, this is faster.
×
1239
        k = i * c + j
×
1240
        # Invariant: @assert firstindex(v) ≤ lo ≤ i + j ≤ k ≤ j ≤ hi ≤ lastindex(v)
1241
        @inbounds v[j], v[k] = v[k], v[j]
×
1242
        i += c - 1
×
1243
    end
×
1244
    count_below, i+hi
×
1245
end
1246

1247
function move!(v, target, source)
×
1248
    # This function never dominates runtime—only add `@inbounds` if you can demonstrate a
1249
    # performance improvement. And if you do, also double check behavior when `target`
1250
    # is out of bounds.
1251
    @assert length(target) == length(source)
×
1252
    if length(target) == 1 || isdisjoint(target, source)
×
1253
        for (i, j) in zip(target, source)
×
1254
            v[i], v[j] = v[j], v[i]
×
1255
        end
×
1256
    else
1257
        @assert minimum(source) <= minimum(target)
×
1258
        reverse!(v, minimum(source), maximum(target))
×
1259
        reverse!(v, minimum(target), maximum(target))
×
1260
    end
1261
end
1262

1263
function _sort!(v::AbstractVector, a::BracketedSort, o::Ordering, kw)
×
1264
    @getkw lo hi scratch
×
1265
    # TODO for further optimization: reuse scratch between trials better, from signpost
1266
    # selection to recursive calls, and from the fallback (but be aware of type stability,
1267
    # especially when sorting IEEE floats.
1268

1269
    # We don't need to bounds check target because that is done higher up in the stack
1270
    # However, we cannot assume the target is inbounds.
1271
    lo < hi || return scratch
×
1272
    ln = hi - lo + 1
×
1273

1274
    # This is simply a precomputed short-circuit to avoid doing scalar math for small inputs.
1275
    # It does not change dispatch at all.
1276
    ln < 260 && return _sort!(v, a.get_next(a.target), o, kw)
×
1277

1278
    target = a.target
×
1279
    k = cbrt(ln)
×
1280
    k2 = round(Int, k^2)
×
1281
    k2ln = k2/ln
×
1282
    offset = .15k*top_set_bit(k2) # TODO for further optimization: tune this
×
1283
    lo_signpost_i, hi_signpost_i =
×
1284
        (floor(Int, (tar - lo) * k2ln + lo + off) for (tar, off) in
1285
            ((minimum(target), -offset), (maximum(target), offset)))
1286
    lastindex_sample = lo+k2-1
×
1287
    expected_middle_ln = (min(lastindex_sample, hi_signpost_i) - max(lo, lo_signpost_i) + 1) / k2ln
×
1288
    # This heuristic is complicated because it fairly accurately reflects the runtime of
1289
    # this algorithm which is necessary to get good dispatch when both the target is large
1290
    # and the input are large.
1291
    # expected_middle_ln is a float and k2 is significantly below typemax(Int), so this will
1292
    # not overflow:
1293
    # TODO move target from alg to kw to avoid this ickyness:
1294
    ln <= 130 + 2k2 + 2expected_middle_ln && return _sort!(v, a.get_next(a.target), o, kw)
×
1295

1296
    # We store the random sample in
1297
    #     sample = view(v, lo:lo+k2)
1298
    # but views are not quite as fast as using the input array directly,
1299
    # so we don't actually construct this view at runtime.
1300

1301
    # TODO for further optimization: handle lots of duplicates better.
1302
    # Right now lots of duplicates rounds up when it could use some super fast optimizations
1303
    # in some cases.
1304
    # e.g.
1305
    #
1306
    # Target:                      |----|
1307
    # Sorted input: 000000000000000000011111112222223333333333
1308
    #
1309
    # Will filter all zeros and ones to the front when it could just take the first few
1310
    # it encounters. This optimization would be especially potent when `allequal(ans)` and
1311
    # equal elements are egal.
1312

1313
    # 3 random trials should typically give us 0.99999 reliability; we can assume
1314
    # the input is pathological and abort to fallback if we fail three trials.
1315
    seed = hash(ln, Int === Int64 ? 0x85eb830e0216012d : 0xae6c4e15)
×
1316
    for attempt in 1:3
×
1317
        seed = hash(attempt, seed)
×
1318
        for i in lo:lo+k2-1
×
1319
            j = mod(hash(i, seed), i:hi) # TODO for further optimization: be sneaky and remove this division
×
1320
            v[i], v[j] = v[j], v[i]
×
1321
        end
×
1322
        count_below, lastindex_middle = if lo_signpost_i <= lo && lastindex_sample <= hi_signpost_i
×
1323
            # The heuristics higher up in this function that dispatch to the `next`
1324
            # algorithm should prevent this from happening.
1325
            # Specifically, this means that expected_middle_ln == ln, so
1326
            # ln <= ... + 2.0expected_middle_ln && return ...
1327
            # will trigger.
1328
            @assert false
×
1329
            # But if it does happen, the kernel reduces to
1330
            0, hi
×
1331
        elseif lo_signpost_i <= lo
×
1332
            _sort!(v, a.get_next(hi_signpost_i), o, (;kw..., hi=lastindex_sample))
×
1333
            bracket_kernel!(v, lo, hi, nothing, v[hi_signpost_i], o)
×
1334
        elseif lastindex_sample <= hi_signpost_i
×
1335
            _sort!(v, a.get_next(lo_signpost_i), o, (;kw..., hi=lastindex_sample))
×
1336
            bracket_kernel!(v, lo, hi, v[lo_signpost_i], nothing, o)
×
1337
        else
1338
            # TODO for further optimization: don't sort the middle elements
1339
            _sort!(v, a.get_next(lo_signpost_i:hi_signpost_i), o, (;kw..., hi=lastindex_sample))
×
1340
            bracket_kernel!(v, lo, hi, v[lo_signpost_i], v[hi_signpost_i], o)
×
1341
        end
1342
        target_in_middle = target .- count_below
×
1343
        if lo <= minimum(target_in_middle) && maximum(target_in_middle) <= lastindex_middle
×
1344
            scratch = _sort!(v, a.get_next(target_in_middle), o, (;kw..., hi=lastindex_middle))
×
1345
            move!(v, target, target_in_middle)
×
1346
            return scratch
×
1347
        end
1348
        # This line almost never runs.
1349
    end
×
1350
    # This line only runs on pathological inputs. Make sure it's covered by tests :)
1351
    _sort!(v, a.get_next(target), o, kw)
×
1352
end
1353

1354

1355
"""
1356
    StableCheckSorted(next) isa Base.Sort.Algorithm
1357

1358
Check if an input is sorted and/or reverse-sorted.
1359

1360
The definition of reverse-sorted is that for every pair of adjacent elements, the latter is
1361
less than the former. This is stricter than `issorted(v, Reverse(o))` to avoid swapping pairs
1362
of elements that compare equal.
1363
"""
1364
struct StableCheckSorted{T<:Algorithm} <: Algorithm
1365
    next::T
1366
end
1367
function _sort!(v::AbstractVector, a::StableCheckSorted, o::Ordering, kw)
89✔
1368
    @getkw lo hi scratch
360✔
1369
    if _issorted(v, lo, hi, o)
2,803✔
1370
        return scratch
210✔
1371
    elseif _issorted(v, lo, hi, Lt((x, y) -> !lt(o, x, y)))
632✔
1372
        # Reverse only if necessary. Using issorted(..., Reverse(o)) would violate stability.
1373
        reverse!(v, lo, hi)
×
1374
        return scratch
×
1375
    end
1376

1377
    _sort!(v, a.next, o, kw)
150✔
1378
end
1379

1380

1381
# The return value indicates whether v is sorted (true) or t is sorted (false)
1382
# This is one of the many reasons radix_sort! is not exported.
1383
function radix_sort!(v::AbstractVector{U}, lo::Integer, hi::Integer, bits::Unsigned,
×
1384
                     t::AbstractVector{U}, offset::Integer,
1385
                     chunk_size=radix_chunk_size_heuristic(lo, hi, bits)) where U <: Unsigned
1386
    # bits is unsigned for performance reasons.
1387
    counts = Vector{Int}(undef, 1 << chunk_size + 1) # TODO use scratch for this
×
1388

1389
    shift = 0
×
1390
    while true
×
1391
        @noinline radix_sort_pass!(t, lo, hi, offset, counts, v, shift, chunk_size)
×
1392
        # the latest data resides in t
1393
        shift += chunk_size
×
1394
        shift < bits || return false
×
1395
        @noinline radix_sort_pass!(v, lo+offset, hi+offset, -offset, counts, t, shift, chunk_size)
×
1396
        # the latest data resides in v
1397
        shift += chunk_size
×
1398
        shift < bits || return true
×
1399
    end
×
1400
end
1401
function radix_sort_pass!(t, lo, hi, offset, counts, v, shift, chunk_size)
×
1402
    mask = UInt(1) << chunk_size - 1  # mask is defined in pass so that the compiler
×
1403
    @inbounds begin                   #  ↳ knows it's shape
×
1404
        # counts[2:mask+2] will store the number of elements that fall into each bucket.
1405
        # if chunk_size = 8, counts[2] is bucket 0x00 and counts[257] is bucket 0xff.
1406
        counts .= 0
×
1407
        for k in lo:hi
×
1408
            x = v[k]                  # lookup the element
×
1409
            i = (x >> shift)&mask + 2 # compute its bucket's index for this pass
×
1410
            counts[i] += 1            # increment that bucket's count
×
1411
        end
×
1412

1413
        counts[1] = lo + offset       # set target index for the first bucket
×
1414
        cumsum!(counts, counts)       # set target indices for subsequent buckets
×
1415
        # counts[1:mask+1] now stores indices where the first member of each bucket
1416
        # belongs, not the number of elements in each bucket. We will put the first element
1417
        # of bucket 0x00 in t[counts[1]], the next element of bucket 0x00 in t[counts[1]+1],
1418
        # and the last element of bucket 0x00 in t[counts[2]-1].
1419

1420
        for k in lo:hi
×
1421
            x = v[k]                  # lookup the element
×
1422
            i = (x >> shift)&mask + 1 # compute its bucket's index for this pass
×
1423
            j = counts[i]             # lookup the target index
×
1424
            t[j] = x                  # put the element where it belongs
×
1425
            counts[i] = j + 1         # increment the target index for the next
×
1426
        end                           #  ↳ element in this bucket
×
1427
    end
1428
end
1429
function radix_chunk_size_heuristic(lo::Integer, hi::Integer, bits::Unsigned)
×
1430
    # chunk_size is the number of bits to radix over at once.
1431
    # We need to allocate an array of size 2^chunk size, and on the other hand the higher
1432
    # the chunk size the fewer passes we need. Theoretically, chunk size should be based on
1433
    # the Lambert W function applied to length. Empirically, we use this heuristic:
1434
    guess = min(10, log(maybe_unsigned(hi-lo))*3/4+3)
×
1435
    # TODO the maximum chunk size should be based on architecture cache size.
1436

1437
    # We need iterations * chunk size ≥ bits, and these cld's
1438
    # make an effort to get iterations * chunk size ≈ bits
1439
    UInt8(cld(bits, cld(bits, guess)))
×
1440
end
1441

1442
maybe_unsigned(x::Integer) = x # this is necessary to avoid calling unsigned on BigInt
×
1443
maybe_unsigned(x::BitSigned) = unsigned(x)
×
1444
function _issorted(v::AbstractVector, lo::Integer, hi::Integer, o::Ordering)
420✔
1445
    @boundscheck checkbounds(v, lo:hi)
510✔
1446
    @inbounds for i in (lo+1):hi
510✔
1447
        lt(o, v[i], v[i-1]) && return false
10,061✔
1448
    end
6,007✔
1449
    true
1450
end
1451

1452

1453
## default sorting policy ##
1454

1455
"""
1456
    InitialOptimizations(next) isa Base.Sort.Algorithm
1457

1458
Attempt to apply a suite of low-cost optimizations to the input vector before sorting. These
1459
optimizations may be automatically applied by the `sort!` family of functions when
1460
`alg=InsertionSort`, `alg=MergeSort`, or `alg=QuickSort` is passed as an argument.
1461

1462
`InitialOptimizations` is an implementation detail and subject to change or removal in
1463
future versions of Julia.
1464

1465
If `next` is stable, then `InitialOptimizations(next)` is also stable.
1466

1467
The specific optimizations attempted by `InitialOptimizations` are
1468
[`SubArrayOptimization`](@ref), [`MissingOptimization`](@ref), [`BoolOptimization`](@ref),
1469
dispatch to [`InsertionSort`](@ref) for inputs with `length <= 10`, and
1470
[`IEEEFloatOptimization`](@ref).
1471
"""
1472
InitialOptimizations(next) = SubArrayOptimization(
×
1473
    MissingOptimization(
1474
        BoolOptimization(
1475
            Small{10}(
1476
                IEEEFloatOptimization(
1477
                    next)))))
1478

1479
"""
1480
    struct DefaultStable <: Algorithm end
1481

1482
`DefaultStable` is an algorithm which indicates that a fast, general purpose sorting
1483
algorithm should be used, but does not specify exactly which algorithm.
1484

1485
Currently, when sorting short NTuples, this is an unrolled mergesort, and otherwise it is
1486
composed of two parts: the [`InitialOptimizations`](@ref) and a hybrid of Radix, Insertion,
1487
Counting, Quick sorts.
1488

1489
We begin with MissingOptimization because it has no runtime cost when it is not
1490
triggered and can enable other optimizations to be applied later. For example,
1491
BoolOptimization cannot apply to an `AbstractVector{Union{Missing, Bool}}`, but after
1492
[`MissingOptimization`](@ref) is applied, that input will be converted into am
1493
`AbstractVector{Bool}`.
1494

1495
We next apply [`BoolOptimization`](@ref) because it also has no runtime cost when it is not
1496
triggered and when it is triggered, it is an incredibly efficient algorithm (sorting `Bool`s
1497
is quite easy).
1498

1499
Next, we dispatch to [`InsertionSort`](@ref) for inputs with `length <= 10`. This dispatch
1500
occurs before the [`IEEEFloatOptimization`](@ref) pass because the
1501
[`IEEEFloatOptimization`](@ref)s are not beneficial for very small inputs.
1502

1503
To conclude the [`InitialOptimizations`](@ref), we apply [`IEEEFloatOptimization`](@ref).
1504

1505
After these optimizations, we branch on whether radix sort and related algorithms can be
1506
applied to the input vector and ordering. We conduct this branch by testing if
1507
`UIntMappable(v, order) !== nothing`. That is, we see if we know of a reversible mapping
1508
from `eltype(v)` to `UInt` that preserves the ordering `order`. We perform this check after
1509
the initial optimizations because they can change the input vector's type and ordering to
1510
make them `UIntMappable`.
1511

1512
If the input is not [`UIntMappable`](@ref), then we perform a presorted check and dispatch
1513
to [`ScratchQuickSort`](@ref).
1514

1515
Otherwise, we dispatch to [`InsertionSort`](@ref) for inputs with `length <= 40` and then
1516
perform a presorted check ([`CheckSorted`](@ref)).
1517

1518
We check for short inputs before performing the presorted check to avoid the overhead of the
1519
check for small inputs. Because the alternate dispatch is to [`InsertionSort`](@ref) which
1520
has efficient `O(n)` runtime on presorted inputs, the check is not necessary for small
1521
inputs.
1522

1523
We check if the input is reverse-sorted for long vectors (more than 500 elements) because
1524
the check is essentially free unless the input is almost entirely reverse sorted.
1525

1526
Note that once the input is determined to be [`UIntMappable`](@ref), we know the order forms
1527
a [total order](wikipedia.org/wiki/Total_order) over the inputs and so it is impossible to
1528
perform an unstable sort because no two elements can compare equal unless they _are_ equal,
1529
in which case switching them is undetectable. We utilize this fact to perform a more
1530
aggressive reverse sorted check that will reverse the vector `[3, 2, 2, 1]`.
1531

1532
After these potential fast-paths are tried and failed, we [`ComputeExtrema`](@ref) of the
1533
input. This computation has a fairly fast `O(n)` runtime, but we still try to delay it until
1534
it is necessary.
1535

1536
Next, we [`ConsiderCountingSort`](@ref). If the range the input is small compared to its
1537
length, we apply [`CountingSort`](@ref).
1538

1539
Next, we [`ConsiderRadixSort`](@ref). This is similar to the dispatch to counting sort,
1540
but we consider the number of _bits_ in the range, rather than the range itself.
1541
Consequently, we apply [`RadixSort`](@ref) for any reasonably long inputs that reach this
1542
stage.
1543

1544
Finally, if the input has length less than 80, we dispatch to [`InsertionSort`](@ref) and
1545
otherwise we dispatch to [`ScratchQuickSort`](@ref).
1546
"""
1547
struct DefaultStable <: Algorithm end
1548

1549
"""
1550
    DEFAULT_STABLE
1551

1552
The default sorting algorithm.
1553

1554
This algorithm is guaranteed to be stable (i.e. it will not reorder elements that compare
1555
equal). It makes an effort to be fast for most inputs.
1556

1557
The algorithms used by `DEFAULT_STABLE` are an implementation detail. See the docstring
1558
of `Base.Sort.DefaultStable` for the current dispatch system.
1559
"""
1560
const DEFAULT_STABLE = DefaultStable()
1561

1562
"""
1563
    DefaultUnstable <: Algorithm
1564

1565
Like [`DefaultStable`](@ref), but does not guarantee stability.
1566
"""
1567
struct DefaultUnstable <: Algorithm end
1568

1569
"""
1570
    DEFAULT_UNSTABLE
1571

1572
An efficient sorting algorithm which may or may not be stable.
1573

1574
The algorithms used by `DEFAULT_UNSTABLE` are an implementation detail. They are currently
1575
the same as those used by [`DEFAULT_STABLE`](@ref), but this is subject to change in future.
1576
"""
1577
const DEFAULT_UNSTABLE = DefaultUnstable()
1578

1579
const _DEFAULT_ALGORITHMS_FOR_VECTORS = InitialOptimizations(
1580
    IsUIntMappable(
1581
        Small{40}(
1582
            CheckSorted(
1583
                ComputeExtrema(
1584
                    ConsiderCountingSort(
1585
                        ConsiderRadixSort(
1586
                            Small{80}(
1587
                                ScratchQuickSort())))))),
1588
        StableCheckSorted(
1589
            ScratchQuickSort())))
1590

1591
_sort!(v::AbstractVector, ::Union{DefaultStable, DefaultUnstable}, o::Ordering, kw) =
14,479✔
1592
    _sort!(v, _DEFAULT_ALGORITHMS_FOR_VECTORS, o, kw)
1593

1594
const SMALL_THRESHOLD  = 20
1595

1596
function Base.show(io::IO, alg::Algorithm)
×
1597
    print_tree(io, alg, 0)
×
1598
end
1599
function print_tree(io::IO, alg::Algorithm, cols::Int)
×
1600
    print(io, "    "^cols)
×
1601
    show_type(io, alg)
×
1602
    print(io, '(')
×
1603
    for (i, name) in enumerate(fieldnames(typeof(alg)))
×
1604
        arg = getproperty(alg, name)
×
1605
        i > 1 && print(io, ',')
×
1606
        if arg isa Algorithm
×
1607
            println(io)
×
1608
            print_tree(io, arg, cols+1)
×
1609
        else
1610
            i > 1 && print(io, ' ')
×
1611
            print(io, arg)
×
1612
        end
1613
    end
×
1614
    print(io, ')')
×
1615
end
1616
show_type(io::IO, alg::Algorithm) = Base.show_type_name(io, typeof(alg).name)
×
1617
show_type(io::IO, alg::Small{N}) where N = print(io, "Base.Sort.Small{$N}")
×
1618

1619
defalg(v::AbstractArray) = DEFAULT_STABLE
11,926✔
1620
defalg(v::AbstractArray{<:Union{Number, Missing}}) = DEFAULT_UNSTABLE
×
1621
defalg(v::AbstractArray{Missing}) = DEFAULT_UNSTABLE # for method disambiguation
×
1622
defalg(v::AbstractArray{Union{}}) = DEFAULT_UNSTABLE # for method disambiguation
×
1623
defalg(v::NTuple) = DEFAULT_STABLE
×
1624

1625
"""
1626
    sort!(v; alg::Base.Sort.Algorithm=Base.Sort.defalg(v), lt=isless, by=identity, rev::Bool=false, order::Base.Order.Ordering=Base.Order.Forward)
1627

1628
Sort the vector `v` in place. A stable algorithm is used by default: the
1629
ordering of elements that compare equal is preserved. A specific algorithm can
1630
be selected via the `alg` keyword (see [Sorting Algorithms](@ref) for available
1631
algorithms).
1632

1633
Elements are first transformed with the function `by` and then compared
1634
according to either the function `lt` or the ordering `order`. Finally, the
1635
resulting order is reversed if `rev=true` (this preserves forward stability:
1636
elements that compare equal are not reversed). The current implementation applies
1637
the `by` transformation before each comparison rather than once per element.
1638

1639
Passing an `lt` other than `isless` along with an `order` other than
1640
[`Base.Order.Forward`](@ref) or [`Base.Order.Reverse`](@ref) is not permitted,
1641
otherwise all options are independent and can be used together in all possible
1642
combinations. Note that `order` can also include a "by" transformation, in
1643
which case it is applied after that defined with the `by` keyword. For more
1644
information on `order` values see the documentation on [Alternate
1645
Orderings](@ref).
1646

1647
Relations between two elements are defined as follows (with "less" and
1648
"greater" exchanged when `rev=true`):
1649

1650
* `x` is less than `y` if `lt(by(x), by(y))` (or `Base.Order.lt(order, by(x), by(y))`) yields true.
1651
* `x` is greater than `y` if `y` is less than `x`.
1652
* `x` and `y` are equivalent if neither is less than the other ("incomparable"
1653
  is sometimes used as a synonym for "equivalent").
1654

1655
The result of `sort!` is sorted in the sense that every element is greater than
1656
or equivalent to the previous one.
1657

1658
The `lt` function must define a strict weak order, that is, it must be
1659

1660
* irreflexive: `lt(x, x)` always yields `false`,
1661
* asymmetric: if `lt(x, y)` yields `true` then `lt(y, x)` yields `false`,
1662
* transitive: `lt(x, y) && lt(y, z)` implies `lt(x, z)`,
1663
* transitive in equivalence: `!lt(x, y) && !lt(y, x)` and `!lt(y, z) && !lt(z,
1664
  y)` together imply `!lt(x, z) && !lt(z, x)`. In words: if `x` and `y` are
1665
  equivalent and `y` and `z` are equivalent then `x` and `z` must be
1666
  equivalent.
1667

1668
For example `<` is a valid `lt` function for `Int` values but `≤` is not: it
1669
violates irreflexivity. For `Float64` values even `<` is invalid as it violates
1670
the fourth condition: `1.0` and `NaN` are equivalent and so are `NaN` and `2.0`
1671
but `1.0` and `2.0` are not equivalent.
1672

1673
See also [`sort`](@ref), [`sortperm`](@ref), [`sortslices`](@ref),
1674
[`partialsort!`](@ref), [`partialsortperm`](@ref), [`issorted`](@ref),
1675
[`searchsorted`](@ref), [`insorted`](@ref), [`Base.Order.ord`](@ref).
1676

1677
# Examples
1678
```jldoctest
1679
julia> v = [3, 1, 2]; sort!(v); v
1680
3-element Vector{Int64}:
1681
 1
1682
 2
1683
 3
1684

1685
julia> v = [3, 1, 2]; sort!(v, rev = true); v
1686
3-element Vector{Int64}:
1687
 3
1688
 2
1689
 1
1690

1691
julia> v = [(1, "c"), (3, "a"), (2, "b")]; sort!(v, by = x -> x[1]); v
1692
3-element Vector{Tuple{Int64, String}}:
1693
 (1, "c")
1694
 (2, "b")
1695
 (3, "a")
1696

1697
julia> v = [(1, "c"), (3, "a"), (2, "b")]; sort!(v, by = x -> x[2]); v
1698
3-element Vector{Tuple{Int64, String}}:
1699
 (3, "a")
1700
 (2, "b")
1701
 (1, "c")
1702

1703
julia> sort(0:3, by=x->x-2, order=Base.Order.By(abs))
1704
4-element Vector{Int64}:
1705
 2
1706
 1
1707
 3
1708
 0
1709

1710
julia> sort(0:3, by=x->x-2, order=Base.Order.By(abs)) == sort(0:3, by=x->abs(x-2))
1711
true
1712

1713
julia> sort([2, NaN, 1, NaN, 3]) # correct sort with default lt=isless
1714
5-element Vector{Float64}:
1715
   1.0
1716
   2.0
1717
   3.0
1718
 NaN
1719
 NaN
1720

1721
julia> sort([2, NaN, 1, NaN, 3], lt=<) # wrong sort due to invalid lt. This behavior is undefined.
1722
5-element Vector{Float64}:
1723
   2.0
1724
 NaN
1725
   1.0
1726
 NaN
1727
   3.0
1728
```
1729
"""
1730
function sort!(v::AbstractVector{T};
14,581✔
1731
               alg::Algorithm=defalg(v),
1732
               lt=isless,
1733
               by=identity,
1734
               rev::Union{Bool,Nothing}=nothing,
1735
               order::Ordering=Forward,
1736
               scratch::Union{Vector{T}, Nothing}=nothing) where T
1737
    _sort!(v, maybe_apply_initial_optimizations(alg), ord(lt,by,rev,order), (;scratch))
14,479✔
1738
    v
12,030✔
1739
end
1740

1741
"""
1742
    sort(v::Union{AbstractVector, NTuple}; alg::Base.Sort.Algorithm=Base.Sort.defalg(v), lt=isless, by=identity, rev::Bool=false, order::Base.Order.Ordering=Base.Order.Forward)
1743

1744
Variant of [`sort!`](@ref) that returns a sorted copy of `v` leaving `v` itself unmodified.
1745

1746
!!! compat "Julia 1.12"
1747
    Sorting `NTuple`s requires Julia 1.12 or later.
1748

1749
# Examples
1750
```jldoctest
1751
julia> v = [3, 1, 2];
1752

1753
julia> sort(v)
1754
3-element Vector{Int64}:
1755
 1
1756
 2
1757
 3
1758

1759
julia> v
1760
3-element Vector{Int64}:
1761
 3
1762
 1
1763
 2
1764
```
1765
"""
1766
sort(v::AbstractVector; kws...) = sort!(copymutable(v); kws...)
1,740✔
1767

1768
function sort(x::NTuple;
×
1769
              alg::Algorithm=defalg(x),
1770
              lt=isless,
1771
              by=identity,
1772
              rev::Union{Bool,Nothing}=nothing,
1773
              order::Ordering=Forward,
1774
              scratch::Union{Vector, Nothing}=nothing)
1775
    # Can't do this check with type parameters because of https://github.com/JuliaLang/julia/issues/56698
1776
    scratch === nothing || eltype(x) == eltype(scratch) || throw(ArgumentError("scratch has the wrong eltype"))
×
1777
    _sort(x, alg, ord(lt,by,rev,order), (;scratch))::typeof(x)
×
1778
end
1779
# Folks who want to hack internals can define a new _sort(x::NTuple, ::TheirAlg, o::Ordering)
1780
# or _sort(x::NTuple{N, TheirType}, ::DefaultStable, o::Ordering) where N
1781
function _sort(x::NTuple, a::Union{DefaultStable, DefaultUnstable}, o::Ordering, kw)
×
1782
    # The unrolled tuple sort is prohibitively slow to compile for length > 9.
1783
    # See https://github.com/JuliaLang/julia/pull/46104#issuecomment-1435688502 for benchmarks
1784
    if length(x) > 9
×
1785
        v = copymutable(x)
×
1786
        _sort!(v, a, o, kw)
×
1787
        typeof(x)(v)
×
1788
    else
1789
        _mergesort(x, o)
×
1790
    end
1791
end
1792
_mergesort(x::Union{NTuple{0}, NTuple{1}}, o::Ordering) = x
×
1793
function _mergesort(x::NTuple, o::Ordering)
×
1794
    a, b = Base.IteratorsMD.split(x, Val(length(x)>>1))
×
1795
    merge(_mergesort(a, o), _mergesort(b, o), o)
×
1796
end
1797
merge(x::NTuple, y::NTuple{0}, o::Ordering) = x
×
1798
merge(x::NTuple{0}, y::NTuple, o::Ordering) = y
×
1799
merge(x::NTuple{0}, y::NTuple{0}, o::Ordering) = x # Method ambiguity
×
1800
merge(x::NTuple, y::NTuple, o::Ordering) =
×
1801
    (lt(o, y[1], x[1]) ? (y[1], merge(x, tail(y), o)...) : (x[1], merge(tail(x), y, o)...))
1802

1803
## partialsortperm: the permutation to sort the first k elements of an array ##
1804

1805
"""
1806
    partialsortperm(v, k; by=identity, lt=isless, rev=false)
1807

1808
Return a partial permutation `I` of the vector `v`, so that `v[I]` returns values of a fully
1809
sorted version of `v` at index `k`. If `k` is a range, a vector of indices is returned; if
1810
`k` is an integer, a single index is returned. The order is specified using the same
1811
keywords as `sort!`. The permutation is stable: the indices of equal elements
1812
will appear in ascending order.
1813

1814
This function is equivalent to, but more efficient than, calling `sortperm(...)[k]`.
1815

1816
# Examples
1817
```jldoctest
1818
julia> v = [3, 1, 2, 1];
1819

1820
julia> v[partialsortperm(v, 1)]
1821
1
1822

1823
julia> p = partialsortperm(v, 1:3)
1824
3-element view(::Vector{Int64}, 1:3) with eltype Int64:
1825
 2
1826
 4
1827
 3
1828

1829
julia> v[p]
1830
3-element Vector{Int64}:
1831
 1
1832
 1
1833
 2
1834
```
1835
"""
1836
partialsortperm(v::AbstractVector, k::Union{Integer,OrdinalRange}; kwargs...) =
×
1837
    partialsortperm!(similar(Vector{eltype(k)}, axes(v,1)), v, k; kwargs...)
1838

1839
"""
1840
    partialsortperm!(ix, v, k; by=identity, lt=isless, rev=false)
1841

1842
Like [`partialsortperm`](@ref), but accepts a preallocated index vector `ix` the same size as
1843
`v`, which is used to store (a permutation of) the indices of `v`.
1844

1845
`ix` is initialized to contain the indices of `v`.
1846

1847
(Typically, the indices of `v` will be `1:length(v)`, although if `v` has an alternative array type
1848
with non-one-based indices, such as an `OffsetArray`, `ix` must share those same indices)
1849

1850
Upon return, `ix` is guaranteed to have the indices `k` in their sorted positions, such that
1851

1852
```julia
1853
partialsortperm!(ix, v, k);
1854
v[ix[k]] == partialsort(v, k)
1855
```
1856

1857
The return value is the `k`th element of `ix` if `k` is an integer, or view into `ix` if `k` is
1858
a range.
1859

1860
$(Base._DOCS_ALIASING_WARNING)
1861

1862
# Examples
1863
```jldoctest
1864
julia> v = [3, 1, 2, 1];
1865

1866
julia> ix = Vector{Int}(undef, 4);
1867

1868
julia> partialsortperm!(ix, v, 1)
1869
2
1870

1871
julia> ix = [1:4;];
1872

1873
julia> partialsortperm!(ix, v, 2:3)
1874
2-element view(::Vector{Int64}, 2:3) with eltype Int64:
1875
 4
1876
 3
1877
```
1878
 """
1879
function partialsortperm!(ix::AbstractVector{<:Integer}, v::AbstractVector,
×
1880
                          k::Union{Integer, OrdinalRange};
1881
                          lt::Function=isless,
1882
                          by::Function=identity,
1883
                          rev::Union{Bool,Nothing}=nothing,
1884
                          order::Ordering=Forward,
1885
                          initialized::Bool=false)
1886
    if axes(ix,1) != axes(v,1)
×
1887
        throw(ArgumentError("The index vector is used as scratch space and must have the " *
×
1888
                            "same length/indices as the source vector, $(axes(ix,1)) != $(axes(v,1))"))
1889
    end
1890
    @inbounds for i in eachindex(ix)
×
1891
        ix[i] = i
×
1892
    end
×
1893

1894
    # do partial quicksort
1895
    _sort!(ix, InitialOptimizations(ScratchQuickSort(k)), Perm(ord(lt, by, rev, order), v), (;))
×
1896

1897
    maybeview(ix, k)
×
1898
end
1899

1900
## sortperm: the permutation to sort an array ##
1901

1902
"""
1903
    sortperm(A; alg::Base.Sort.Algorithm=Base.Sort.DEFAULT_UNSTABLE, lt=isless, by=identity, rev::Bool=false, order::Base.Order.Ordering=Base.Order.Forward, [dims::Integer])
1904

1905
Return a permutation vector or array `I` that puts `A[I]` in sorted order along the given dimension.
1906
If `A` has more than one dimension, then the `dims` keyword argument must be specified. The order is specified
1907
using the same keywords as [`sort!`](@ref). The permutation is guaranteed to be stable even
1908
if the sorting algorithm is unstable: the indices of equal elements will appear in
1909
ascending order.
1910

1911
See also [`sortperm!`](@ref), [`partialsortperm`](@ref), [`invperm`](@ref), [`indexin`](@ref).
1912
To sort slices of an array, refer to [`sortslices`](@ref).
1913

1914
!!! compat "Julia 1.9"
1915
    The method accepting `dims` requires at least Julia 1.9.
1916

1917
# Examples
1918
```jldoctest
1919
julia> v = [3, 1, 2];
1920

1921
julia> p = sortperm(v)
1922
3-element Vector{Int64}:
1923
 2
1924
 3
1925
 1
1926

1927
julia> v[p]
1928
3-element Vector{Int64}:
1929
 1
1930
 2
1931
 3
1932

1933
julia> A = [8 7; 5 6]
1934
2×2 Matrix{Int64}:
1935
 8  7
1936
 5  6
1937

1938
julia> sortperm(A, dims = 1)
1939
2×2 Matrix{Int64}:
1940
 2  4
1941
 1  3
1942

1943
julia> sortperm(A, dims = 2)
1944
2×2 Matrix{Int64}:
1945
 3  1
1946
 2  4
1947
```
1948
"""
UNCOV
1949
function sortperm(A::AbstractArray;
×
1950
                  alg::Algorithm=DEFAULT_UNSTABLE,
1951
                  lt=isless,
1952
                  by=identity,
1953
                  rev::Union{Bool,Nothing}=nothing,
1954
                  order::Ordering=Forward,
1955
                  scratch::Union{Vector{<:Integer}, Nothing}=nothing,
1956
                  dims...) #to optionally specify dims argument
1957
    if rev === true
×
UNCOV
1958
        _sortperm(A; alg, order=ord(lt, by, true, order), scratch, dims...)
×
1959
    else
1960
        _sortperm(A; alg, order=ord(lt, by, nothing, order), scratch, dims...)
×
1961
    end
1962
end
UNCOV
1963
function _sortperm(A::AbstractArray; alg, order, scratch, dims...)
×
1964
    if order === Forward && isa(A,Vector) && eltype(A)<:Integer
×
1965
        n = length(A)
×
1966
        if n > 1
×
1967
            min, max = extrema(A)
×
1968
            (diff, o1) = sub_with_overflow(max, min)
×
1969
            (rangelen, o2) = add_with_overflow(diff, oneunit(diff))
×
1970
            if !(o1 || o2)::Bool && rangelen < div(n,2)
×
1971
                return sortperm_int_range(A, rangelen, min)
×
1972
            end
1973
        end
1974
    end
UNCOV
1975
    ix = copymutable(LinearIndices(A))
×
UNCOV
1976
    sort!(ix; alg, order = Perm(order, vec(A)), scratch, dims...)
×
1977
end
1978

1979

1980
"""
1981
    sortperm!(ix, A; alg::Base.Sort.Algorithm=Base.Sort.DEFAULT_UNSTABLE, lt=isless, by=identity, rev::Bool=false, order::Base.Order.Ordering=Base.Order.Forward, [dims::Integer])
1982

1983
Like [`sortperm`](@ref), but accepts a preallocated index vector or array `ix` with the same `axes` as `A`.
1984
`ix` is initialized to contain the values `LinearIndices(A)`.
1985

1986
$(Base._DOCS_ALIASING_WARNING)
1987

1988
!!! compat "Julia 1.9"
1989
    The method accepting `dims` requires at least Julia 1.9.
1990

1991
# Examples
1992
```jldoctest
1993
julia> v = [3, 1, 2]; p = zeros(Int, 3);
1994

1995
julia> sortperm!(p, v); p
1996
3-element Vector{Int64}:
1997
 2
1998
 3
1999
 1
2000

2001
julia> v[p]
2002
3-element Vector{Int64}:
2003
 1
2004
 2
2005
 3
2006

2007
julia> A = [8 7; 5 6]; p = zeros(Int,2, 2);
2008

2009
julia> sortperm!(p, A; dims=1); p
2010
2×2 Matrix{Int64}:
2011
 2  4
2012
 1  3
2013

2014
julia> sortperm!(p, A; dims=2); p
2015
2×2 Matrix{Int64}:
2016
 3  1
2017
 2  4
2018
```
2019
"""
2020
@inline function sortperm!(ix::AbstractArray{T}, A::AbstractArray;
×
2021
                   alg::Algorithm=DEFAULT_UNSTABLE,
2022
                   lt=isless,
2023
                   by=identity,
2024
                   rev::Union{Bool,Nothing}=nothing,
2025
                   order::Ordering=Forward,
2026
                   initialized::Bool=false,
2027
                   scratch::Union{Vector{T}, Nothing}=nothing,
2028
                   dims...) where T <: Integer #to optionally specify dims argument
2029
    (typeof(A) <: AbstractVector) == (:dims in keys(dims)) && throw(ArgumentError("Dims argument incorrect for type $(typeof(A))"))
×
2030
    axes(ix) == axes(A) || throw(ArgumentError("index array must have the same size/axes as the source array, $(axes(ix)) != $(axes(A))"))
×
2031

2032
    ix .= LinearIndices(A)
×
2033
    if rev === true
×
2034
        sort!(ix; alg, order=Perm(ord(lt, by, true, order), vec(A)), scratch, dims...)
×
2035
    else
2036
        sort!(ix; alg, order=Perm(ord(lt, by, nothing, order), vec(A)), scratch, dims...)
×
2037
    end
2038
end
2039

2040
# sortperm for vectors of few unique integers
2041
function sortperm_int_range(x::Vector{<:Integer}, rangelen, minval)
×
2042
    offs = 1 - minval
×
2043
    n = length(x)
×
2044

2045
    counts = fill(0, rangelen+1)
×
2046
    counts[1] = 1
×
2047
    @inbounds for i = 1:n
×
2048
        counts[x[i] + offs + 1] += 1
×
2049
    end
×
2050

2051
    #cumsum!(counts, counts)
2052
    @inbounds for i = 2:length(counts)
×
2053
        counts[i] += counts[i-1]
×
2054
    end
×
2055

2056
    P = Vector{Int}(undef, n)
×
2057
    @inbounds for i = 1:n
×
2058
        label = x[i] + offs
×
2059
        P[counts[label]] = i
×
2060
        counts[label] += 1
×
2061
    end
×
2062

2063
    return P
×
2064
end
2065

2066
## sorting multi-dimensional arrays ##
2067

2068
"""
2069
    sort(A; dims::Integer, alg::Base.Sort.Algorithm=Base.Sort.defalg(A), lt=isless, by=identity, rev::Bool=false, order::Base.Order.Ordering=Base.Order.Forward)
2070

2071
Sort a multidimensional array `A` along the given dimension.
2072
See [`sort!`](@ref) for a description of possible
2073
keyword arguments.
2074

2075
To sort slices of an array, refer to [`sortslices`](@ref).
2076

2077
# Examples
2078
```jldoctest
2079
julia> A = [4 3; 1 2]
2080
2×2 Matrix{Int64}:
2081
 4  3
2082
 1  2
2083

2084
julia> sort(A, dims = 1)
2085
2×2 Matrix{Int64}:
2086
 1  2
2087
 4  3
2088

2089
julia> sort(A, dims = 2)
2090
2×2 Matrix{Int64}:
2091
 3  4
2092
 1  2
2093
```
2094
"""
2095
function sort(A::AbstractArray{T};
×
2096
              dims::Integer,
2097
              alg::Algorithm=defalg(A),
2098
              lt=isless,
2099
              by=identity,
2100
              rev::Union{Bool,Nothing}=nothing,
2101
              order::Ordering=Forward,
2102
              scratch::Union{Vector{T}, Nothing}=nothing) where T
2103
    dim = dims
×
2104
    order = ord(lt,by,rev,order)
×
2105
    n = length(axes(A, dim))
×
2106
    if dim != 1
×
2107
        pdims = (dim, setdiff(1:ndims(A), dim)...)  # put the selected dimension first
×
2108
        Ap = permutedims(A, pdims)
×
2109
        Av = vec(Ap)
×
2110
        sort_chunks!(Av, n, maybe_apply_initial_optimizations(alg), order, scratch)
×
2111
        permutedims(Ap, invperm(pdims))
×
2112
    else
2113
        Av = A[:]
×
2114
        sort_chunks!(Av, n, maybe_apply_initial_optimizations(alg), order, scratch)
×
2115
        reshape(Av, axes(A))
×
2116
    end
2117
end
2118

2119
@noinline function sort_chunks!(Av, n, alg, order, scratch)
×
2120
    inds = LinearIndices(Av)
×
2121
    sort_chunks!(Av, n, alg, order, scratch, first(inds), last(inds))
×
2122
end
2123

2124
@noinline function sort_chunks!(Av, n, alg, order, scratch::Nothing, fst, lst)
×
2125
    for lo = fst:n:lst
×
2126
        s = _sort!(Av, alg, order, (; lo, hi=lo+n-1, scratch))
×
2127
        s !== nothing && return sort_chunks!(Av, n, alg, order, s, lo+n, lst)
×
2128
    end
×
2129
    Av
×
2130
end
2131

2132
@noinline function sort_chunks!(Av, n, alg, order, scratch::AbstractVector, fst, lst)
×
2133
    for lo = fst:n:lst
×
2134
        _sort!(Av, alg, order, (; lo, hi=lo+n-1, scratch))
×
2135
    end
×
2136
    Av
×
2137
end
2138

2139

2140
"""
2141
    sort!(A; dims::Integer, alg::Base.Sort.Algorithm=Base.Sort.defalg(A), lt=isless, by=identity, rev::Bool=false, order::Base.Order.Ordering=Base.Order.Forward)
2142

2143
Sort the multidimensional array `A` along dimension `dims`.
2144
See the one-dimensional version of [`sort!`](@ref) for a description of
2145
possible keyword arguments.
2146

2147
To sort slices of an array, refer to [`sortslices`](@ref).
2148

2149
!!! compat "Julia 1.1"
2150
    This function requires at least Julia 1.1.
2151

2152
# Examples
2153
```jldoctest
2154
julia> A = [4 3; 1 2]
2155
2×2 Matrix{Int64}:
2156
 4  3
2157
 1  2
2158

2159
julia> sort!(A, dims = 1); A
2160
2×2 Matrix{Int64}:
2161
 1  2
2162
 4  3
2163

2164
julia> sort!(A, dims = 2); A
2165
2×2 Matrix{Int64}:
2166
 1  2
2167
 3  4
2168
```
2169
"""
2170
function sort!(A::AbstractArray{T};
×
2171
               dims::Integer,
2172
               alg::Algorithm=defalg(A),
2173
               lt=isless,
2174
               by=identity,
2175
               rev::Union{Bool,Nothing}=nothing,
2176
               order::Ordering=Forward, # TODO stop eagerly over-allocating.
2177
               scratch::Union{Vector{T}, Nothing}=size(A, dims) < 10 ? nothing : Vector{T}(undef, size(A, dims))) where T
2178
    nd = ndims(A)
×
2179
    1 <= dims <= nd || throw(ArgumentError("dimension out of range"))
×
2180
    alg2 = maybe_apply_initial_optimizations(alg)
×
2181
    order2 = ord(lt, by, rev, order)
×
2182
    foreach(ntuple(Val, nd)) do d
×
2183
        get_value(d) == dims || return
×
2184
        # We assume that an Integer between 1 and nd must be equal to one of the
2185
        # values 1:nd. If this assumption is false, then what's an integer? and
2186
        # also sort! will silently do nothing.
2187

2188
        idxs = CartesianIndices(ntuple(i -> i == get_value(d) ? 1 : axes(A, i), ndims(A)))
×
2189
        get_view(idx) = view(A, ntuple(i -> i == get_value(d) ? Colon() : idx[i], ndims(A))...)
×
2190
        if d == Val(1) || size(A, get_value(d)) < 30
×
2191
            for idx in idxs
×
2192
                sort!(get_view(idx); alg=alg2, order=order2, scratch)
×
2193
            end
×
2194
        else
2195
            v = similar(get_view(first(idxs)))
×
2196
            for idx in idxs
×
2197
                vw = get_view(idx)
×
2198
                v .= vw
×
2199
                sort!(v; alg=alg2, order=order2, scratch)
×
2200
                vw .= v
×
2201
            end
×
2202
        end
2203
        A
×
2204
    end
2205
    A
×
2206
end
2207
get_value(::Val{x}) where x = x
×
2208

2209

2210
## uint mapping to allow radix sorting primitives other than UInts ##
2211

2212
"""
2213
    UIntMappable(T::Type, order::Base.Order.Ordering)
2214

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

2218
If either is not implemented, return `nothing`.
2219
"""
2220
UIntMappable(T::Type, order::Ordering) = nothing
55✔
2221

2222
"""
2223
    uint_map(x, order::Base.Order.Ordering)::Unsigned
2224

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

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

2232
See also: [`UIntMappable`](@ref) [`uint_unmap`](@ref)
2233
"""
2234
function uint_map end
2235

2236
"""
2237
    uint_unmap(T::Type, u::Unsigned, order::Base.Order.Ordering)
2238

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

2242
See also: [`uint_map`](@ref) [`UIntMappable`](@ref)
2243
"""
2244
function uint_unmap end
2245

2246

2247
### Primitive Types
2248

2249
# Integers
2250
uint_map(x::Unsigned, ::ForwardOrdering) = x
×
2251
uint_unmap(::Type{T}, u::T, ::ForwardOrdering) where T <: Unsigned = u
×
2252

2253
uint_map(x::Signed, ::ForwardOrdering) =
×
2254
    unsigned(xor(x, typemin(x)))
2255
uint_unmap(::Type{T}, u::Unsigned, ::ForwardOrdering) where T <: Signed =
×
2256
    xor(signed(u), typemin(T))
2257

2258
UIntMappable(T::BitIntegerType, ::ForwardOrdering) = unsigned(T)
×
2259

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

2264
# Chars
2265
uint_map(x::Char, ::ForwardOrdering) = reinterpret(UInt32, x)
×
2266
uint_unmap(::Type{Char}, u::UInt32, ::ForwardOrdering) = reinterpret(Char, u)
×
2267
UIntMappable(::Type{Char}, ::ForwardOrdering) = UInt32
×
2268

2269
### Reverse orderings
2270
uint_map(x, rev::ReverseOrdering) = ~uint_map(x, rev.fwd)
×
2271
uint_unmap(T::Type, u::Unsigned, rev::ReverseOrdering) = uint_unmap(T, ~u, rev.fwd)
×
2272
UIntMappable(T::Type, order::ReverseOrdering) = UIntMappable(T, order.fwd)
×
2273

2274

2275
### Vectors
2276

2277
# Convert v to unsigned integers in place, maintaining sort order.
2278
function uint_map!(v::AbstractVector, lo::Integer, hi::Integer, order::Ordering)
×
2279
    u = reinterpret(UIntMappable(eltype(v), order), v)
×
2280
    @inbounds for i in lo:hi
×
2281
        u[i] = uint_map(v[i], order)
×
2282
    end
×
2283
    u
×
2284
end
2285

2286
function uint_unmap!(v::AbstractVector, u::AbstractVector{U}, lo::Integer, hi::Integer,
×
2287
                     order::Ordering, offset::U=zero(U),
2288
                     index_offset::Integer=0) where U <: Unsigned
2289
    @inbounds for i in lo:hi
×
2290
        v[i] = uint_unmap(eltype(v), u[i+index_offset]+offset, order)
×
2291
    end
×
2292
    v
×
2293
end
2294

2295

2296

2297
### Unused constructs for backward compatibility ###
2298

2299
## Old algorithms ##
2300

2301
struct QuickSortAlg     <: Algorithm end
2302
struct MergeSortAlg     <: Algorithm end
2303

2304
"""
2305
    PartialQuickSort{T <: Union{Integer,OrdinalRange}}
2306

2307
Indicate that a sorting function should use the partial quick sort algorithm.
2308
`PartialQuickSort(k)` is like `QuickSort`, but is only required to find and
2309
sort the elements that would end up in `v[k]` were `v` fully sorted.
2310

2311
Characteristics:
2312
  * *not stable*: does not preserve the ordering of elements that
2313
    compare equal (e.g. "a" and "A" in a sort of letters that
2314
    ignores case).
2315
  * *in-place* in memory.
2316
  * *divide-and-conquer*: sort strategy similar to [`MergeSort`](@ref).
2317

2318
Note that `PartialQuickSort(k)` does not necessarily sort the whole array. For example,
2319

2320
```jldoctest
2321
julia> x = rand(100);
2322

2323
julia> k = 50:100;
2324

2325
julia> s1 = sort(x; alg=QuickSort);
2326

2327
julia> s2 = sort(x; alg=PartialQuickSort(k));
2328

2329
julia> map(issorted, (s1, s2))
2330
(true, false)
2331

2332
julia> map(x->issorted(x[k]), (s1, s2))
2333
(true, true)
2334

2335
julia> s1[k] == s2[k]
2336
true
2337
```
2338
"""
2339
struct PartialQuickSort{T <: Union{Integer,OrdinalRange}} <: Algorithm
2340
    k::T
2341
end
2342

2343
"""
2344
    QuickSort
2345

2346
Indicate that a sorting function should use the quick sort
2347
algorithm, which is *not* stable.
2348

2349
Characteristics:
2350
  * *not stable*: does not preserve the ordering of elements that
2351
    compare equal (e.g. "a" and "A" in a sort of letters that
2352
    ignores case).
2353
  * *in-place* in memory.
2354
  * *divide-and-conquer*: sort strategy similar to [`MergeSort`](@ref).
2355
  * *good performance* for large collections.
2356
"""
2357
const QuickSort     = QuickSortAlg()
2358

2359
"""
2360
    MergeSort
2361

2362
Indicate that a sorting function should use the merge sort
2363
algorithm. Merge sort divides the collection into
2364
subcollections and repeatedly merges them, sorting each
2365
subcollection at each step, until the entire
2366
collection has been recombined in sorted form.
2367

2368
Characteristics:
2369
  * *stable*: preserves the ordering of elements that compare
2370
    equal (e.g. "a" and "A" in a sort of letters that ignores
2371
    case).
2372
  * *not in-place* in memory.
2373
  * *divide-and-conquer* sort strategy.
2374
  * *good performance* for large collections but typically not quite as
2375
    fast as [`QuickSort`](@ref).
2376
"""
2377
const MergeSort     = MergeSortAlg()
2378

2379
maybe_apply_initial_optimizations(alg::Algorithm) = alg
×
2380
maybe_apply_initial_optimizations(alg::QuickSortAlg) = InitialOptimizations(alg)
×
2381
maybe_apply_initial_optimizations(alg::MergeSortAlg) = InitialOptimizations(alg)
×
2382
maybe_apply_initial_optimizations(alg::InsertionSortAlg) = InitialOptimizations(alg)
×
2383

2384
# selectpivot!
2385
#
2386
# Given 3 locations in an array (lo, mi, and hi), sort v[lo], v[mi], v[hi] and
2387
# choose the middle value as a pivot
2388
#
2389
# Upon return, the pivot is in v[lo], and v[hi] is guaranteed to be
2390
# greater than the pivot
2391

2392
@inline function selectpivot!(v::AbstractVector, lo::Integer, hi::Integer, o::Ordering)
×
2393
    @inbounds begin
×
2394
        mi = midpoint(lo, hi)
×
2395

2396
        # sort v[mi] <= v[lo] <= v[hi] such that the pivot is immediately in place
2397
        if lt(o, v[lo], v[mi])
×
2398
            v[mi], v[lo] = v[lo], v[mi]
×
2399
        end
2400

2401
        if lt(o, v[hi], v[lo])
×
2402
            if lt(o, v[hi], v[mi])
×
2403
                v[hi], v[lo], v[mi] = v[lo], v[mi], v[hi]
×
2404
            else
2405
                v[hi], v[lo] = v[lo], v[hi]
×
2406
            end
2407
        end
2408

2409
        # return the pivot
2410
        return v[lo]
×
2411
    end
2412
end
2413

2414
# partition!
2415
#
2416
# select a pivot, and partition v according to the pivot
2417

2418
function partition!(v::AbstractVector, lo::Integer, hi::Integer, o::Ordering)
×
2419
    pivot = selectpivot!(v, lo, hi, o)
×
2420
    # pivot == v[lo], v[hi] > pivot
2421
    i, j = lo, hi
×
2422
    @inbounds while true
×
2423
        i += 1; j -= 1
×
2424
        while lt(o, v[i], pivot); i += 1; end;
×
2425
        while lt(o, pivot, v[j]); j -= 1; end;
×
2426
        i >= j && break
×
2427
        v[i], v[j] = v[j], v[i]
×
2428
    end
×
2429
    v[j], v[lo] = pivot, v[j]
×
2430

2431
    # v[j] == pivot
2432
    # v[k] >= pivot for k > j
2433
    # v[i] <= pivot for i < j
2434
    return j
×
2435
end
2436

2437
function sort!(v::AbstractVector, lo::Integer, hi::Integer, a::QuickSortAlg, o::Ordering)
×
2438
    @inbounds while lo < hi
×
2439
        hi-lo <= SMALL_THRESHOLD && return sort!(v, lo, hi, SMALL_ALGORITHM, o)
×
2440
        j = partition!(v, lo, hi, o)
×
2441
        if j-lo < hi-j
×
2442
            # recurse on the smaller chunk
2443
            # this is necessary to preserve O(log(n))
2444
            # stack space in the worst case (rather than O(n))
2445
            lo < (j-1) && sort!(v, lo, j-1, a, o)
×
2446
            lo = j+1
×
2447
        else
2448
            j+1 < hi && sort!(v, j+1, hi, a, o)
×
2449
            hi = j-1
×
2450
        end
2451
    end
×
2452
    return v
×
2453
end
2454

2455
sort!(v::AbstractVector{T}, lo::Integer, hi::Integer, a::MergeSortAlg, o::Ordering, t0::Vector{T}) where T =
×
2456
    invoke(sort!, Tuple{typeof.((v, lo, hi, a, o))..., AbstractVector{T}}, v, lo, hi, a, o, t0) # For disambiguation
2457
function sort!(v::AbstractVector{T}, lo::Integer, hi::Integer, a::MergeSortAlg, o::Ordering,
×
2458
        t0::Union{AbstractVector{T}, Nothing}=nothing) where T
2459
    @inbounds if lo < hi
×
2460
        hi-lo <= SMALL_THRESHOLD && return sort!(v, lo, hi, SMALL_ALGORITHM, o)
×
2461

2462
        m = midpoint(lo, hi)
×
2463

2464
        t = t0 === nothing ? similar(v, m-lo+1) : t0
×
2465
        length(t) < m-lo+1 && resize!(t, m-lo+1)
×
2466
        Base.require_one_based_indexing(t)
×
2467

2468
        sort!(v, lo,  m,  a, o, t)
×
2469
        sort!(v, m+1, hi, a, o, t)
×
2470

2471
        i, j = 1, lo
×
2472
        while j <= m
×
2473
            t[i] = v[j]
×
2474
            i += 1
×
2475
            j += 1
×
2476
        end
×
2477

2478
        i, k = 1, lo
×
2479
        while k < j <= hi
×
2480
            if lt(o, v[j], t[i])
×
2481
                v[k] = v[j]
×
2482
                j += 1
×
2483
            else
2484
                v[k] = t[i]
×
2485
                i += 1
×
2486
            end
2487
            k += 1
×
2488
        end
×
2489
        while k < j
×
2490
            v[k] = t[i]
×
2491
            k += 1
×
2492
            i += 1
×
2493
        end
×
2494
    end
2495

2496
    return v
×
2497
end
2498

2499
function sort!(v::AbstractVector, lo::Integer, hi::Integer, a::PartialQuickSort,
×
2500
               o::Ordering)
2501
    @inbounds while lo < hi
×
2502
        hi-lo <= SMALL_THRESHOLD && return sort!(v, lo, hi, SMALL_ALGORITHM, o)
×
2503
        j = partition!(v, lo, hi, o)
×
2504

2505
        if j <= first(a.k)
×
2506
            lo = j+1
×
2507
        elseif j >= last(a.k)
×
2508
            hi = j-1
×
2509
        else
2510
            # recurse on the smaller chunk
2511
            # this is necessary to preserve O(log(n))
2512
            # stack space in the worst case (rather than O(n))
2513
            if j-lo < hi-j
×
2514
                lo < (j-1) && sort!(v, lo, j-1, a, o)
×
2515
                lo = j+1
×
2516
            else
2517
                hi > (j+1) && sort!(v, j+1, hi, a, o)
×
2518
                hi = j-1
×
2519
            end
2520
        end
2521
    end
×
2522
    return v
×
2523
end
2524

2525
## Old extensibility mechanisms ##
2526

2527
# Support 3-, 5-, and 6-argument versions of sort! for calling into the internals in the old way
2528
sort!(v::AbstractVector, a::Algorithm, o::Ordering) = sort!(v, firstindex(v), lastindex(v), a, o)
×
2529
function sort!(v::AbstractVector, lo::Integer, hi::Integer, a::Algorithm, o::Ordering)
×
2530
    _sort!(v, a, o, (; lo, hi, legacy_dispatch_entry=a))
×
2531
    v
×
2532
end
2533
sort!(v::AbstractVector, lo::Integer, hi::Integer, a::Algorithm, o::Ordering, _) = sort!(v, lo, hi, a, o)
×
2534
function sort!(v::AbstractVector, lo::Integer, hi::Integer, a::Algorithm, o::Ordering, scratch::Vector)
×
2535
    _sort!(v, a, o, (; lo, hi, scratch, legacy_dispatch_entry=a))
×
2536
    v
×
2537
end
2538

2539
# Support dispatch on custom algorithms in the old way
2540
# sort!(::AbstractVector, ::Integer, ::Integer, ::MyCustomAlgorithm, ::Ordering) = ...
2541
function _sort!(v::AbstractVector, a::Algorithm, o::Ordering, kw)
×
2542
    @getkw lo hi scratch legacy_dispatch_entry
×
2543
    if legacy_dispatch_entry === a
×
2544
        # This error prevents infinite recursion for unknown algorithms
2545
        throw(ArgumentError(LazyString("Base.Sort._sort!(::", typeof(v), ", ::", typeof(a), ", ::", typeof(o), ", ::Any) is not defined")))
×
2546
    else
2547
        sort!(v, lo, hi, a, o)
×
2548
        scratch
×
2549
    end
2550
end
2551

2552
end # module Sort
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc