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
8 changed files
with
311 additions
and
6 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
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,140 @@ | ||
# 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 | ||
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} | ||
new{P}(p) | ||
end | ||
end | ||
function natural_predecessor(@nospecialize o::PositiveInteger) | ||
getfield(o, :predecessor) # avoid specializing `getproperty` for each number | ||
end | ||
end | ||
|
||
module IntegersGreaterThanOne | ||
using ..PositiveIntegers | ||
export | ||
IntegerGreaterThanOne, IntegerGreaterThanOneUpperBound, | ||
natural_predecessor_predecessor | ||
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 natural_predecessor_predecessor(@nospecialize x::IntegerGreaterThanOne) | ||
natural_predecessor(natural_predecessor(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 half_floor, half_ceiling | ||
@assume_effects :foldable :nothrow function half_floor(@nospecialize m::NonnegativeInteger) | ||
if m isa IntegerGreaterThanOneUpperBound | ||
let n = natural_predecessor_predecessor(m), rec = half_floor(n) | ||
natural_successor(rec) | ||
end | ||
else | ||
n0 | ||
end | ||
end | ||
@assume_effects :foldable :nothrow function half_ceiling(@nospecialize m::NonnegativeInteger) | ||
if m isa IntegerGreaterThanOneUpperBound | ||
let n = natural_predecessor_predecessor(m), rec = half_ceiling(n) | ||
natural_successor(rec) | ||
end | ||
else | ||
if m isa PositiveIntegerUpperBound | ||
n1 | ||
else | ||
n0 | ||
end | ||
end | ||
end | ||
end | ||
end | ||
|
||
module _TypeDomainNumberTupleUtils | ||
using | ||
.._TypeDomainNumbers.PositiveIntegers, .._TypeDomainNumbers.IntegersGreaterThanOne, | ||
.._TypeDomainNumbers.Constants, .._TypeDomainNumbers.Utils, .._TupleTypeByLength | ||
using Base: @assume_effects, front, tail | ||
export tuple_type_domain_length, split_tuple_into_halves, skip_from_front, skip_from_tail | ||
@assume_effects :foldable :nothrow function tuple_type_domain_length(@nospecialize tup::Tuple) | ||
if tup isa Tuple1OrMore | ||
let t = tail(tup), rec = tuple_type_domain_length(t) | ||
natural_successor(rec) | ||
end | ||
else | ||
n0 | ||
end | ||
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 split_tuple_into_halves(@nospecialize tup::Tuple) | ||
len = tuple_type_domain_length(tup) | ||
len_l = half_floor(len) | ||
len_r = half_ceiling(len) | ||
tup_l = skip_from_tail(tup, len_r) | ||
tup_r = skip_from_front(tup, len_l) | ||
(tup_l, tup_r) | ||
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
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,31 @@ | ||
# This file is a part of Julia. License is MIT: https://julialang.org/license | ||
|
||
using | ||
Test, | ||
Base._TypeDomainNumbers.PositiveIntegers, | ||
Base._TypeDomainNumbers.IntegersGreaterThanOne, | ||
Base._TypeDomainNumbers.Constants, | ||
Base._TypeDomainNumberTupleUtils | ||
|
||
@testset "type domain numbers" begin | ||
@test n0 isa NonnegativeInteger | ||
@test n1 isa NonnegativeInteger | ||
@test n1 isa PositiveInteger | ||
@testset "succ" begin | ||
for x ∈ (n0, n1) | ||
@test x === natural_predecessor(@inferred natural_successor(x)) | ||
@test x === natural_predecessor_predecessor(natural_successor(natural_successor(x))) | ||
end | ||
end | ||
@testset "type safety" begin | ||
@test_throws TypeError PositiveInteger{Int} | ||
end | ||
@testset "tuple utils" begin | ||
@test n0 === @inferred tuple_type_domain_length(()) | ||
@test n1 === @inferred tuple_type_domain_length((7,)) | ||
@test ((), ()) === @inferred split_tuple_into_halves(()) | ||
@test ((), (7,)) === @inferred split_tuple_into_halves((7,)) | ||
@test ((3,), (7,)) === @inferred split_tuple_into_halves((3, 7)) | ||
@test ((3,), (7, 9)) === @inferred split_tuple_into_halves((3, 7, 9)) | ||
end | ||
end |