forked from JuliaLang/julia
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Uses merge sort, as an obvious choice for a stable sort of tuples. A recursive data structure of singleton type, representing Peano natural numbers, is used to help with splitting a tuple into two halves in the merge sort. An alternative design would use a reference tuple, but this would require relying on `tail`, which seems more harsh on the compiler. With the recursive datastructure the predecessor operation and the successor operation are both trivial. Allows inference to preserve inferred element type even when tuple length is not known. Follow-up PRs may add further improvements, such as the ability to select an unstable sorting algorithm. The added file, typedomainnumbers.jl is not specific to sorting, thus making it a separate file. Xref JuliaLang#55571. Fixes JuliaLang#54489
- Loading branch information
Showing
5 changed files
with
326 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
# This file is a part of Julia. License is MIT: https://julialang.org/license | ||
|
||
# Adapted from the TypeDomainNaturalNumbers.jl package. | ||
module _TypeDomainNumbers | ||
module Zeros | ||
export Zero | ||
struct Zero end | ||
end | ||
|
||
module PositiveIntegers | ||
module RecursiveStep | ||
using ...Zeros | ||
export recursive_step | ||
function recursive_step(@nospecialize t::Type) | ||
Union{Zero, t} | ||
end | ||
end | ||
module UpperBounds | ||
using ..RecursiveStep | ||
abstract type A end | ||
abstract type B{P <: recursive_step(A)} <: A end | ||
abstract type C{P <: recursive_step(B)} <: B{P} end | ||
abstract type D{P <: recursive_step(C)} <: C{P} end | ||
end | ||
using .RecursiveStep | ||
const PositiveIntegerUpperBound = UpperBounds.A | ||
const PositiveIntegerUpperBoundTighter = UpperBounds.D | ||
export | ||
natural_successor, natural_predecessor, | ||
NonnegativeInteger, NonnegativeIntegerUpperBound, | ||
PositiveInteger, PositiveIntegerUpperBound, | ||
type_assert_nonnegative_integer, type_assert_positive_integer | ||
struct PositiveInteger{ | ||
Predecessor <: recursive_step(PositiveIntegerUpperBoundTighter), | ||
} <: PositiveIntegerUpperBoundTighter{Predecessor} | ||
predecessor::Predecessor | ||
global const NonnegativeInteger = recursive_step(PositiveInteger) | ||
global const NonnegativeIntegerUpperBound = recursive_step(PositiveIntegerUpperBound) | ||
global function natural_successor(p::P) where {P <: NonnegativeInteger} | ||
ret = new{P}(p) | ||
type_assert_positive_integer(ret) | ||
end | ||
end | ||
function type_assert_nonnegative_integer(@nospecialize x::NonnegativeInteger) | ||
x | ||
end | ||
function type_assert_positive_integer(@nospecialize x::PositiveInteger) | ||
x | ||
end | ||
function natural_predecessor(@nospecialize o::PositiveInteger) | ||
ret = getfield(o, :predecessor) # avoid specializing `getproperty` for each number | ||
type_assert_nonnegative_integer(ret) | ||
end | ||
end | ||
|
||
module IntegersGreaterThanOne | ||
using ..PositiveIntegers | ||
export | ||
IntegerGreaterThanOne, IntegerGreaterThanOneUpperBound, | ||
type_assert_integer_greater_than_1 | ||
const IntegerGreaterThanOne = let t = PositiveInteger | ||
t{P} where {P <: t} | ||
end | ||
const IntegerGreaterThanOneUpperBound = let t = PositiveIntegerUpperBound | ||
PositiveIntegers.UpperBounds.B{P} where {P <: t} | ||
end | ||
function type_assert_integer_greater_than_1(@nospecialize x::IntegerGreaterThanOne) | ||
x | ||
end | ||
end | ||
|
||
module Constants | ||
using ..Zeros, ..PositiveIntegers | ||
export n0, n1 | ||
const n0 = Zero() | ||
const n1 = natural_successor(n0) | ||
end | ||
|
||
module Utils | ||
using ..PositiveIntegers, ..IntegersGreaterThanOne, ..Constants | ||
using Base: @assume_effects | ||
export minus_two, half_floor, half_ceiling, half_floor_nontrivial, half_ceiling_nontrivial | ||
function minus_two(@nospecialize m::IntegerGreaterThanOne) | ||
natural_predecessor(natural_predecessor(m)) | ||
end | ||
@assume_effects :foldable :nothrow function half_floor(@nospecialize m::NonnegativeInteger) | ||
ret = if m isa IntegerGreaterThanOneUpperBound | ||
let n = minus_two(m), rec = @inline half_floor(n) | ||
type_assert_positive_integer(natural_successor(rec)) | ||
end | ||
else | ||
n0 | ||
end | ||
type_assert_nonnegative_integer(ret) | ||
end | ||
@assume_effects :foldable :nothrow function half_ceiling(@nospecialize m::NonnegativeInteger) | ||
ret = if m isa IntegerGreaterThanOneUpperBound | ||
let n = minus_two(m), rec = @inline half_ceiling(n) | ||
type_assert_positive_integer(natural_successor(rec)) | ||
end | ||
else | ||
if m isa PositiveIntegerUpperBound | ||
n1 | ||
else | ||
n0 | ||
end | ||
end | ||
type_assert_nonnegative_integer(ret) | ||
end | ||
function half_floor_nontrivial(@nospecialize m::IntegerGreaterThanOne) | ||
ret = half_floor(m) | ||
type_assert_positive_integer(ret) | ||
end | ||
function half_ceiling_nontrivial(@nospecialize m::IntegerGreaterThanOne) | ||
ret = half_ceiling(m) | ||
type_assert_positive_integer(ret) | ||
end | ||
end | ||
end | ||
|
||
module _TupleTypeByLength | ||
export | ||
Tuple1OrMore, Tuple2OrMore, Tuple3OrMore, Tuple4OrMore, Tuple32OrMore, | ||
type_assert_tuple_0_or_more, type_assert_tuple_1_or_more, type_assert_tuple_2_or_more, | ||
type_assert_tuple_3_or_more, type_assert_tuple_4_or_more, | ||
type_assert_tuple_1 | ||
const Tuple1OrMore = Tuple{Any, Vararg} | ||
const Tuple2OrMore = Tuple{Any, Any, Vararg} | ||
const Tuple3OrMore = Tuple{Any, Any, Any, Vararg} | ||
const Tuple4OrMore = Tuple{Any, Any, Any, Any, Vararg} | ||
const Tuple32OrMore = Base.Any32 | ||
function type_assert_tuple_0_or_more(@nospecialize x::Tuple) | ||
x | ||
end | ||
function type_assert_tuple_1_or_more(@nospecialize x::Tuple1OrMore) | ||
x | ||
end | ||
function type_assert_tuple_2_or_more(@nospecialize x::Tuple2OrMore) | ||
x | ||
end | ||
function type_assert_tuple_3_or_more(@nospecialize x::Tuple3OrMore) | ||
x | ||
end | ||
function type_assert_tuple_4_or_more(@nospecialize x::Tuple4OrMore) | ||
x | ||
end | ||
end | ||
|
||
module _TypeDomainNumberTupleUtils | ||
using | ||
.._TypeDomainNumbers.PositiveIntegers, .._TypeDomainNumbers.IntegersGreaterThanOne, | ||
.._TypeDomainNumbers.Constants, .._TupleTypeByLength | ||
using Base: @assume_effects, front, tail | ||
export | ||
tuple_type_domain_length, | ||
skip_from_front, skip_from_tail, | ||
skip_from_front_nontrivial, skip_from_tail_nontrivial | ||
# The `@nospecialize` and `@inline` together should effectively result in specializing | ||
# on the length, without specializing on the types of the elements. | ||
@assume_effects :foldable :nothrow function tuple_type_domain_length(@nospecialize tup::Tuple) | ||
ret = if tup isa Tuple1OrMore | ||
let t = tail(tup), rec = @inline tuple_type_domain_length(t) | ||
type_assert_positive_integer(natural_successor(rec)) | ||
end | ||
else | ||
n0 | ||
end | ||
type_assert_nonnegative_integer(ret) | ||
end | ||
@assume_effects :foldable function skip_from_front((@nospecialize tup::Tuple), @nospecialize skip_count::NonnegativeInteger) | ||
if skip_count isa PositiveIntegerUpperBound | ||
let cm1 = natural_predecessor(skip_count), t = tail(tup) | ||
@inline skip_from_front(t, cm1) | ||
end | ||
else | ||
tup | ||
end | ||
end | ||
@assume_effects :foldable function skip_from_tail((@nospecialize tup::Tuple), @nospecialize skip_count::NonnegativeInteger) | ||
if skip_count isa PositiveIntegerUpperBound | ||
let cm1 = natural_predecessor(skip_count), t = front(tup) | ||
@inline skip_from_tail(t, cm1) | ||
end | ||
else | ||
tup | ||
end | ||
end | ||
function skip_from_front_nontrivial((@nospecialize tup::Tuple2OrMore), @nospecialize skip_count::PositiveInteger) | ||
skip_from_front(tup, skip_count) | ||
end | ||
function skip_from_tail_nontrivial((@nospecialize tup::Tuple2OrMore), @nospecialize skip_count::PositiveInteger) | ||
skip_from_tail(tup, skip_count) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters