From d4e955c42d6635d80f0d1cdf91abe063a3045db7 Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Thu, 16 May 2024 10:38:09 -0500 Subject: [PATCH 1/9] Add sort for NTuples, make it extensible py packages, make DefaultStable and DefaultUnstable dispatchable, and add tests --- base/sort.jl | 96 ++++++++++++++++++++++++++++++++++++++----------- test/sorting.jl | 43 ++++++++++++++++++---- 2 files changed, 112 insertions(+), 27 deletions(-) diff --git a/base/sort.jl b/base/sort.jl index 0aeb77699275a..35e0da1e94999 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -4,7 +4,7 @@ module Sort using Base.Order -using Base: copymutable, midpoint, require_one_based_indexing, uinttype, +using Base: copymutable, midpoint, require_one_based_indexing, uinttype, tail, sub_with_overflow, add_with_overflow, OneTo, BitSigned, BitIntegerType, top_set_bit import Base: @@ -1475,21 +1475,16 @@ InitialOptimizations(next) = SubArrayOptimization( Small{10}( IEEEFloatOptimization( next))))) -""" - DEFAULT_STABLE - -The default sorting algorithm. - -This algorithm is guaranteed to be stable (i.e. it will not reorder elements that compare -equal). It makes an effort to be fast for most inputs. -The algorithms used by `DEFAULT_STABLE` are an implementation detail. See extended help -for the current dispatch system. +""" +struct DefaultStable <: Algorithm end -# Extended Help +`DefaultStable` is an algorithm which indicates that a fast, genreal purpose sorting +algorithm should be used, but does not specify exactly which algorith. -`DEFAULT_STABLE` is composed of two parts: the [`InitialOptimizations`](@ref) and a hybrid -of Radix, Insertion, Counting, Quick sorts. +Currently, when sorting short NTuples, this is an unrolled mergesort, and otherwise it is +composed of two parts: the [`InitialOptimizations`](@ref) and a hybrid of Radix, Insertion, +Counting, Quick sorts. We begin with MissingOptimization because it has no runtime cost when it is not triggered and can enable other optimizations to be applied later. For example, @@ -1549,7 +1544,39 @@ stage. Finally, if the input has length less than 80, we dispatch to [`InsertionSort`](@ref) and otherwise we dispatch to [`ScratchQuickSort`](@ref). """ -const DEFAULT_STABLE = InitialOptimizations( +struct DefaultStable <: Algorithm end + +""" + DEFAULT_STABLE + +The default sorting algorithm. + +This algorithm is guaranteed to be stable (i.e. it will not reorder elements that compare +equal). It makes an effort to be fast for most inputs. + +The algorithms used by `DEFAULT_STABLE` are an implementation detail. See the extended help +of `Base.Sort.DefaultStable` for the current dispatch system. +""" +const DEFAULT_STABLE = DefaultStable() + +""" + DefaultUnstable <: Algorithm + +Like [`DefaultStable`](@ref), but does not guarantee stability. +""" +struct DefaultUnstable <: Algorithm end + +""" + DEFAULT_UNSTABLE + +An efficient sorting algorithm which may or may not be stable. + +The algorithms used by `DEFAULT_UNSTABLE` are an implementation detail. They are currently +the same as those used by [`DEFAULT_STABLE`](@ref), but this is subject to change in future. +""" +const DEFAULT_UNSTABLE = DefaultUnstable() + +const _DEFAULT_ALGORITHMS_FOR_VECTORS = InitialOptimizations( IsUIntMappable( Small{40}( CheckSorted( @@ -1560,15 +1587,10 @@ const DEFAULT_STABLE = InitialOptimizations( ScratchQuickSort())))))), StableCheckSorted( ScratchQuickSort()))) -""" - DEFAULT_UNSTABLE -An efficient sorting algorithm. +_sort!(v::AbstractVector, ::Union{DefaultStable, DefaultUnstable}, o::Ordering, kw) = + _sort!(v, _DEFAULT_ALGORITHMS_FOR_VECTORS, o, kw) -The algorithms used by `DEFAULT_UNSTABLE` are an implementation detail. They are currently -the same as those used by [`DEFAULT_STABLE`](@ref), but this is subject to change in future. -""" -const DEFAULT_UNSTABLE = DEFAULT_STABLE const SMALL_THRESHOLD = 20 function Base.show(io::IO, alg::Algorithm) @@ -1598,6 +1620,7 @@ defalg(v::AbstractArray) = DEFAULT_STABLE defalg(v::AbstractArray{<:Union{Number, Missing}}) = DEFAULT_UNSTABLE defalg(v::AbstractArray{Missing}) = DEFAULT_UNSTABLE # for method disambiguation defalg(v::AbstractArray{Union{}}) = DEFAULT_UNSTABLE # for method disambiguation +defalg(v::NTuple) = DEFAULT_STABLE """ sort!(v; alg::Base.Sort.Algorithm=Base.Sort.defalg(v), lt=isless, by=identity, rev::Bool=false, order::Base.Order.Ordering=Base.Order.Forward) @@ -1736,6 +1759,37 @@ julia> v """ sort(v::AbstractVector; kws...) = sort!(copymutable(v); kws...) +function sort(x::NTuple{N,T}; + alg::Algorithm=defalg(x), + lt=isless, + by=identity, + rev::Union{Bool,Nothing}=nothing, + order::Ordering=Forward, + scratch::Union{Vector{T}, Nothing}=nothing) where {N,T} + _sort(x, alg, ord(lt,by,rev,order), (;scratch)) +end +# Folks who want to hack internals can define a new _sort(x::NTuple, ::TheirAlg, o::Ordering) +# or _sort(x::NTuple{N, TheirType}, ::DefaultStable, o::Ordering) where N +function _sort(x::NTuple, a::Union{DefaultStable, DefaultUnstable}, o::Ordering, kw) + if length(x) > 9 + v = copymutable(x) + _sort!(v, a, o, kw) + typeof(x)(v) + else + _mergesort(x, o) + end +end +_mergesort(x::Union{NTuple{0}, NTuple{1}}, o::Ordering) = x +function _mergesort(x::NTuple, o::Ordering) + a, b = Base.IteratorsMD.split(x, Val(length(x)>>1)) + merge(_mergesort(a, o), _mergesort(b, o), o) +end +merge(x::NTuple, y::NTuple{0}, o::Ordering) = x +merge(x::NTuple{0}, y::NTuple, o::Ordering) = y +merge(x::NTuple{0}, y::NTuple{0}, o::Ordering) = x # Method ambiguity +merge(x::NTuple, y::NTuple, o::Ordering) = + (lt(o, y[1], x[1]) ? (y[1], merge(x, tail(y), o)...) : (x[1], merge(tail(x), y, o)...)) + ## partialsortperm: the permutation to sort the first k elements of an array ## """ diff --git a/test/sorting.jl b/test/sorting.jl index 2714197f58823..7accb42ae0495 100644 --- a/test/sorting.jl +++ b/test/sorting.jl @@ -94,6 +94,20 @@ end vcat(2000, (x:x+99 for x in 1900:-100:100)..., 1:99) end +function tuple_sort_test(x) + @test issorted(sort(x)) + length(x) > 9 && return # length > 9 uses a vector fallback + @test 0 == @allocated sort(x) +end +@testset "sort(::NTuple)" begin + @test sort((9,8,3,3,6,2,0,8)) == (0,2,3,3,6,8,8,9) + @test sort((9,8,3,3,6,2,0,8), by=x->x÷3) == (2,0,3,3,8,6,8,9) + for i in 1:40 + tuple_sort_test(rand(NTuple{i, Float64})) + end + @test_throws MethodError sort((1,2,3.0)) +end + @testset "partialsort" begin @test partialsort([3,6,30,1,9],3) == 6 @test partialsort([3,6,30,1,9],3:4) == [6,9] @@ -799,9 +813,9 @@ end let requires_uint_mappable = Union{Base.Sort.RadixSort, Base.Sort.ConsiderRadixSort, Base.Sort.CountingSort, Base.Sort.ConsiderCountingSort, - typeof(Base.Sort.DEFAULT_STABLE.next.next.next.big.next.yes), - typeof(Base.Sort.DEFAULT_STABLE.next.next.next.big.next.yes.big), - typeof(Base.Sort.DEFAULT_STABLE.next.next.next.big.next.yes.big.next)} + typeof(Base.Sort._DEFAULT_ALGORITHMS_FOR_VECTORS.next.next.next.big.next.yes), + typeof(Base.Sort._DEFAULT_ALGORITHMS_FOR_VECTORS.next.next.next.big.next.yes.big), + typeof(Base.Sort._DEFAULT_ALGORITHMS_FOR_VECTORS.next.next.next.big.next.yes.big.next)} function test_alg(kw, alg, float=true) for order in [Base.Forward, Base.Reverse, Base.By(x -> x^2)] @@ -841,15 +855,18 @@ end end end - test_alg_rec(Base.DEFAULT_STABLE) + test_alg_rec(Base.Sort._DEFAULT_ALGORITHMS_FOR_VECTORS) end end @testset "show(::Algorithm)" begin - @test eval(Meta.parse(string(Base.DEFAULT_STABLE))) === Base.DEFAULT_STABLE - lines = split(string(Base.DEFAULT_STABLE), '\n') + @test eval(Meta.parse(string(Base.Sort._DEFAULT_ALGORITHMS_FOR_VECTORS))) === Base.Sort._DEFAULT_ALGORITHMS_FOR_VECTORS + lines = split(string(Base.Sort._DEFAULT_ALGORITHMS_FOR_VECTORS), '\n') @test 10 < maximum(length, lines) < 100 @test 1 < length(lines) < 30 + + @test eval(Meta.parse(string(Base.DEFAULT_STABLE))) === Base.DEFAULT_STABLE + @test string(Base.DEFAULT_STABLE) == "Base.Sort.DefaultStable()" end @testset "Extensibility" begin @@ -890,6 +907,20 @@ end end @test sort([1,2,3], alg=MySecondAlg()) == [9,9,9] @test all(sort(v, alg=Base.Sort.InitialOptimizations(MySecondAlg())) .=== vcat(fill(9, 100), fill(missing, 10))) + + # Tuple extensions (custom alg) + @test_throws MethodError sort((1,2,3), alg=MyFirstAlg()) + Base.Sort._sort(v::NTuple, ::MyFirstAlg, o::Base.Order.Ordering, kw) = "hi!" + @test sort((1,2,3), alg=MyFirstAlg()) == "hi!" + + struct TupleFoo + x::Int + end + + # Tuple extensions (custom type) + @test_throws MethodError sort(TupleFoo.((3,1,2))) + Base.Sort._sort(v::NTuple{N, TupleFoo}, ::Base.Sort.DefaultStable, o::Base.Order.Ordering, kw) where N = v + @test sort(TupleFoo.((3,1,2))) === TupleFoo.((3,1,2)) end @testset "sort!(v, lo, hi, alg, order)" begin From dae5920c9d838384e1d00e2232870edc23cc0519 Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Thu, 16 May 2024 16:19:22 +0000 Subject: [PATCH 2/9] Fix typo --- base/sort.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/sort.jl b/base/sort.jl index 35e0da1e94999..274e19151603c 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -1480,7 +1480,7 @@ InitialOptimizations(next) = SubArrayOptimization( struct DefaultStable <: Algorithm end `DefaultStable` is an algorithm which indicates that a fast, genreal purpose sorting -algorithm should be used, but does not specify exactly which algorith. +algorithm should be used, but does not specify exactly which algorithm. Currently, when sorting short NTuples, this is an unrolled mergesort, and otherwise it is composed of two parts: the [`InitialOptimizations`](@ref) and a hybrid of Radix, Insertion, From 88ef163cf361e68cccec22e92573062a0fbd15f9 Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Fri, 17 May 2024 10:44:11 +0000 Subject: [PATCH 3/9] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Lars Göttgens --- base/sort.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/sort.jl b/base/sort.jl index 274e19151603c..b00276a341629 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -1477,9 +1477,9 @@ InitialOptimizations(next) = SubArrayOptimization( next))))) """ -struct DefaultStable <: Algorithm end + struct DefaultStable <: Algorithm end -`DefaultStable` is an algorithm which indicates that a fast, genreal purpose sorting +`DefaultStable` is an algorithm which indicates that a fast, general purpose sorting algorithm should be used, but does not specify exactly which algorithm. Currently, when sorting short NTuples, this is an unrolled mergesort, and otherwise it is From 60dcc0e6a53636278949f877ac6c7996ad399530 Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Fri, 17 May 2024 12:00:40 +0000 Subject: [PATCH 4/9] Explain where 9 came from --- base/sort.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/base/sort.jl b/base/sort.jl index b00276a341629..654d5f6fb9b4b 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -1771,7 +1771,9 @@ end # Folks who want to hack internals can define a new _sort(x::NTuple, ::TheirAlg, o::Ordering) # or _sort(x::NTuple{N, TheirType}, ::DefaultStable, o::Ordering) where N function _sort(x::NTuple, a::Union{DefaultStable, DefaultUnstable}, o::Ordering, kw) - if length(x) > 9 + # The unrolled tuple sort is prohibitively slow to compile for length > 9. + # See https://github.com/JuliaLang/julia/pull/46104#issuecomment-1435688502 for benchmarks + if length(x) > 9 v = copymutable(x) _sort!(v, a, o, kw) typeof(x)(v) From cfe4fc7c76b0ee7474b910819dd4ca06c70f7e08 Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Tue, 2 Jul 2024 15:11:19 -0500 Subject: [PATCH 5/9] whitespace --- base/sort.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/sort.jl b/base/sort.jl index 654d5f6fb9b4b..0a5d0c7038d1d 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -1773,7 +1773,7 @@ end function _sort(x::NTuple, a::Union{DefaultStable, DefaultUnstable}, o::Ordering, kw) # The unrolled tuple sort is prohibitively slow to compile for length > 9. # See https://github.com/JuliaLang/julia/pull/46104#issuecomment-1435688502 for benchmarks - if length(x) > 9 + if length(x) > 9 v = copymutable(x) _sort!(v, a, o, kw) typeof(x)(v) From c7acc41b1b21c0a67a8b6dbaa473380bc8541938 Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Sun, 3 Nov 2024 07:59:24 -0600 Subject: [PATCH 6/9] Hint the compiler that `sort(::NTuple)` is type stable to improve abstract inference --- base/sort.jl | 2 +- test/sorting.jl | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/base/sort.jl b/base/sort.jl index b4af7442718d1..4a31e50accabe 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -1766,7 +1766,7 @@ function sort(x::NTuple{N,T}; rev::Union{Bool,Nothing}=nothing, order::Ordering=Forward, scratch::Union{Vector{T}, Nothing}=nothing) where {N,T} - _sort(x, alg, ord(lt,by,rev,order), (;scratch)) + _sort(x, alg, ord(lt,by,rev,order), (;scratch))::typeof(x) end # Folks who want to hack internals can define a new _sort(x::NTuple, ::TheirAlg, o::Ordering) # or _sort(x::NTuple{N, TheirType}, ::DefaultStable, o::Ordering) where N diff --git a/test/sorting.jl b/test/sorting.jl index 7accb42ae0495..ccf44b335b9d8 100644 --- a/test/sorting.jl +++ b/test/sorting.jl @@ -106,6 +106,7 @@ end tuple_sort_test(rand(NTuple{i, Float64})) end @test_throws MethodError sort((1,2,3.0)) + @test Base.infer_return_type(sort, Tuple{Tuple{Vararg{Int}}}) == Tuple{Vararg{Int}} end @testset "partialsort" begin From 24d0944c5b9611c259eac5a2160439172bd9ab9f Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Sun, 3 Nov 2024 08:03:33 -0600 Subject: [PATCH 7/9] add news --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index ba9ca1c521c55..41619c3f8075c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -119,6 +119,7 @@ New library features * `Base.require_one_based_indexing` and `Base.has_offset_axes` are now public ([#56196]) * New `ltruncate`, `rtruncate` and `ctruncate` functions for truncating strings to text width, accounting for char widths ([#55351]) * `isless` (and thus `cmp`, sorting, etc.) is now supported for zero-dimensional `AbstractArray`s ([#55772]) +* `sort` now supports `NTuple`s ([#54494]) Standard library changes ------------------------ From ed51be63226ae7b38e99430be0c70e57a79e0c80 Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Sat, 23 Nov 2024 07:40:03 -0600 Subject: [PATCH 8/9] Remove tuple sorting --- NEWS.md | 1 - base/sort.jl | 41 +++-------------------------------------- test/sorting.jl | 29 ----------------------------- 3 files changed, 3 insertions(+), 68 deletions(-) diff --git a/NEWS.md b/NEWS.md index fd9975bc8c53d..535d14208f0b8 100644 --- a/NEWS.md +++ b/NEWS.md @@ -119,7 +119,6 @@ New library features * `Base.require_one_based_indexing` and `Base.has_offset_axes` are now public ([#56196]) * New `ltruncate`, `rtruncate` and `ctruncate` functions for truncating strings to text width, accounting for char widths ([#55351]) * `isless` (and thus `cmp`, sorting, etc.) is now supported for zero-dimensional `AbstractArray`s ([#55772]) -* `sort` now supports `NTuple`s ([#54494]) Standard library changes ------------------------ diff --git a/base/sort.jl b/base/sort.jl index 4a0edc442df38..ab760396b43ec 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -4,7 +4,7 @@ module Sort using Base.Order -using Base: copymutable, midpoint, require_one_based_indexing, uinttype, tail, +using Base: copymutable, midpoint, require_one_based_indexing, uinttype, sub_with_overflow, add_with_overflow, OneTo, BitSigned, BitIntegerType, top_set_bit import Base: @@ -1482,9 +1482,8 @@ InitialOptimizations(next) = SubArrayOptimization( `DefaultStable` is an algorithm which indicates that a fast, general purpose sorting algorithm should be used, but does not specify exactly which algorithm. -Currently, when sorting short NTuples, this is an unrolled mergesort, and otherwise it is -composed of two parts: the [`InitialOptimizations`](@ref) and a hybrid of Radix, Insertion, -Counting, Quick sorts. +Currently, it is composed of two parts: the [`InitialOptimizations`](@ref) and a hybrid of +Radix, Insertion, Counting, Quick sorts. We begin with MissingOptimization because it has no runtime cost when it is not triggered and can enable other optimizations to be applied later. For example, @@ -1620,7 +1619,6 @@ defalg(v::AbstractArray) = DEFAULT_STABLE defalg(v::AbstractArray{<:Union{Number, Missing}}) = DEFAULT_UNSTABLE defalg(v::AbstractArray{Missing}) = DEFAULT_UNSTABLE # for method disambiguation defalg(v::AbstractArray{Union{}}) = DEFAULT_UNSTABLE # for method disambiguation -defalg(v::NTuple) = DEFAULT_STABLE """ sort!(v; alg::Base.Sort.Algorithm=Base.Sort.defalg(v), lt=isless, by=identity, rev::Bool=false, order::Base.Order.Ordering=Base.Order.Forward) @@ -1759,39 +1757,6 @@ julia> v """ sort(v::AbstractVector; kws...) = sort!(copymutable(v); kws...) -function sort(x::NTuple{N,T}; - alg::Algorithm=defalg(x), - lt=isless, - by=identity, - rev::Union{Bool,Nothing}=nothing, - order::Ordering=Forward, - scratch::Union{Vector{T}, Nothing}=nothing) where {N,T} - _sort(x, alg, ord(lt,by,rev,order), (;scratch))::typeof(x) -end -# Folks who want to hack internals can define a new _sort(x::NTuple, ::TheirAlg, o::Ordering) -# or _sort(x::NTuple{N, TheirType}, ::DefaultStable, o::Ordering) where N -function _sort(x::NTuple, a::Union{DefaultStable, DefaultUnstable}, o::Ordering, kw) - # The unrolled tuple sort is prohibitively slow to compile for length > 9. - # See https://github.com/JuliaLang/julia/pull/46104#issuecomment-1435688502 for benchmarks - if length(x) > 9 - v = copymutable(x) - _sort!(v, a, o, kw) - typeof(x)(v) - else - _mergesort(x, o) - end -end -_mergesort(x::Union{NTuple{0}, NTuple{1}}, o::Ordering) = x -function _mergesort(x::NTuple, o::Ordering) - a, b = Base.IteratorsMD.split(x, Val(length(x)>>1)) - merge(_mergesort(a, o), _mergesort(b, o), o) -end -merge(x::NTuple, y::NTuple{0}, o::Ordering) = x -merge(x::NTuple{0}, y::NTuple, o::Ordering) = y -merge(x::NTuple{0}, y::NTuple{0}, o::Ordering) = x # Method ambiguity -merge(x::NTuple, y::NTuple, o::Ordering) = - (lt(o, y[1], x[1]) ? (y[1], merge(x, tail(y), o)...) : (x[1], merge(tail(x), y, o)...)) - ## partialsortperm: the permutation to sort the first k elements of an array ## """ diff --git a/test/sorting.jl b/test/sorting.jl index 266058bb30c23..71af50429027a 100644 --- a/test/sorting.jl +++ b/test/sorting.jl @@ -94,21 +94,6 @@ end vcat(2000, (x:x+99 for x in 1900:-100:100)..., 1:99) end -function tuple_sort_test(x) - @test issorted(sort(x)) - length(x) > 9 && return # length > 9 uses a vector fallback - @test 0 == @allocated sort(x) -end -@testset "sort(::NTuple)" begin - @test sort((9,8,3,3,6,2,0,8)) == (0,2,3,3,6,8,8,9) - @test sort((9,8,3,3,6,2,0,8), by=x->x÷3) == (2,0,3,3,8,6,8,9) - for i in 1:40 - tuple_sort_test(rand(NTuple{i, Float64})) - end - @test_throws MethodError sort((1,2,3.0)) - @test Base.infer_return_type(sort, Tuple{Tuple{Vararg{Int}}}) == Tuple{Vararg{Int}} -end - @testset "partialsort" begin @test partialsort([3,6,30,1,9],3) == 6 @test partialsort([3,6,30,1,9],3:4) == [6,9] @@ -928,20 +913,6 @@ end end @test sort([1,2,3], alg=MySecondAlg()) == [9,9,9] @test all(sort(v, alg=Base.Sort.InitialOptimizations(MySecondAlg())) .=== vcat(fill(9, 100), fill(missing, 10))) - - # Tuple extensions (custom alg) - @test_throws MethodError sort((1,2,3), alg=MyFirstAlg()) - Base.Sort._sort(v::NTuple, ::MyFirstAlg, o::Base.Order.Ordering, kw) = "hi!" - @test sort((1,2,3), alg=MyFirstAlg()) == "hi!" - - struct TupleFoo - x::Int - end - - # Tuple extensions (custom type) - @test_throws MethodError sort(TupleFoo.((3,1,2))) - Base.Sort._sort(v::NTuple{N, TupleFoo}, ::Base.Sort.DefaultStable, o::Base.Order.Ordering, kw) where N = v - @test sort(TupleFoo.((3,1,2))) === TupleFoo.((3,1,2)) end @testset "sort!(v, lo, hi, alg, order)" begin From 5c92cb9863dcfbb16de9b1382a1f22837f867d10 Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Sat, 23 Nov 2024 07:59:04 -0600 Subject: [PATCH 9/9] docs fixup --- base/sort.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/sort.jl b/base/sort.jl index ab760396b43ec..29e67a3eb8d8c 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -1553,7 +1553,7 @@ The default sorting algorithm. This algorithm is guaranteed to be stable (i.e. it will not reorder elements that compare equal). It makes an effort to be fast for most inputs. -The algorithms used by `DEFAULT_STABLE` are an implementation detail. See the extended help +The algorithms used by `DEFAULT_STABLE` are an implementation detail. See the docstring of `Base.Sort.DefaultStable` for the current dispatch system. """ const DEFAULT_STABLE = DefaultStable()