https://github.com/JuliaLang/julia
Tip revision: 1a8513cbb5166c3d096043de9d6e28e8e14ebb4d authored by Kristoffer Carlsson on 09 July 2022, 19:40:59 UTC
only locate packages in envs at or above in the load path where they were identified
only locate packages in envs at or above in the load path where they were identified
Tip revision: 1a8513c
missing.jl
# This file is a part of Julia. License is MIT: https://julialang.org/license
isdefined(Main, :OffsetArrays) || @eval Main include("testhelpers/OffsetArrays.jl")
using .Main.OffsetArrays
@testset "MissingException" begin
@test sprint(showerror, MissingException("test")) == "MissingException: test"
end
@testset "nonmissingtype" begin
@test nonmissingtype(Union{Int, Missing}) == Int
@test nonmissingtype(Union{Rational, Missing}) == Rational
@test nonmissingtype(Any) == Any
@test nonmissingtype(Missing) == Union{}
end
@testset "convert" begin
@test convert(Union{Int, Missing}, 1) === 1
@test convert(Union{Int, Missing}, 1.0) === 1
@test convert(Union{Nothing, Missing}, missing) === missing
@test convert(Union{Nothing, Missing}, nothing) === nothing
@test convert(Union{Missing, Nothing, Float64}, 1) === 1.0
@test_throws MethodError convert(Missing, 1)
@test_throws MethodError convert(Union{Nothing, Missing}, 1)
@test_throws MethodError convert(Union{Int, Missing}, "a")
end
@testset "promote rules" begin
@test promote_type(Missing, Missing) == Missing
@test promote_type(Missing, Int) == Union{Missing, Int}
@test promote_type(Int, Missing) == Union{Missing, Int}
@test promote_type(Int, Any) == Any
@test promote_type(Any, Any) == Any
@test promote_type(Missing, Any) == Any
@test promote_type(Any, Missing) == Any
@test promote_type(Union{Int, Missing}, Missing) == Union{Int, Missing}
@test promote_type(Missing, Union{Int, Missing}) == Union{Int, Missing}
@test promote_type(Union{Int, Missing}, Int) == Union{Int, Missing}
@test promote_type(Int, Union{Int, Missing}) == Union{Int, Missing}
@test promote_type(Any, Union{Int, Missing}) == Any
@test promote_type(Union{Nothing, Missing}, Any) == Any
@test promote_type(Union{Int, Missing}, Union{Int, Missing}) == Union{Int, Missing}
@test promote_type(Union{Float64, Missing}, Union{String, Missing}) == Any
@test promote_type(Union{Float64, Missing}, Union{Int, Missing}) == Union{Float64, Missing}
@test_broken promote_type(Union{Nothing, Missing, Int}, Float64) == Any
end
@testset "promotion in various contexts" for T in (Nothing, Missing)
@test collect(v for v in (1, T())) isa Vector{Union{Int,T}}
@test map(identity, Any[1, T()]) isa Vector{Union{Int,T}}
@test broadcast(identity, Any[1, T()]) isa Vector{Union{Int,T}}
@test unique((1, T())) isa Vector{Union{Int,T}}
@test map(ismissing, Any[1, missing]) isa Vector{Bool}
@test broadcast(ismissing, Any[1, missing]) isa BitVector
end
@testset "comparison operators" begin
@test (missing == missing) === missing
@test (1 == missing) === missing
@test (missing == 1) === missing
@test (missing != missing) === missing
@test (1 != missing) === missing
@test (missing != 1) === missing
@test isequal(missing, missing)
@test !isequal(1, missing)
@test !isequal(missing, 1)
@test (missing < missing) === missing
@test (missing < 1) === missing
@test (1 < missing) === missing
@test (missing <= missing) === missing
@test (missing <= 1) === missing
@test (1 <= missing) === missing
@test !isless(missing, missing)
@test !isless(missing, 1)
@test isless(1, missing)
@test (missing ≈ missing) === missing
@test isapprox(missing, 1.0, atol=1e-6) === missing
@test isapprox(1.0, missing, rtol=1e-6) === missing
@test !any(T -> T === Union{Missing,Bool}, Base.return_types(isequal, Tuple{Any,Any}))
end
@testset "arithmetic operators" begin
arithmetic_operators = [+, -, *, /, ^, Base.div, Base.mod, Base.fld, Base.rem]
# All unary operators return missing when evaluating missing
for f in [!, ~, +, -, *, &, |, xor, nand, nor]
@test ismissing(f(missing))
end
# All arithmetic operators return missing when operating on two missing's
# All arithmetic operators return missing when operating on a scalar and an missing
# All arithmetic operators return missing when operating on an missing and a scalar
for f in arithmetic_operators
@test ismissing(f(missing, missing))
@test ismissing(f(1, missing))
@test ismissing(f(missing, 1))
end
@test ismissing(min(missing, missing))
@test ismissing(max(missing, missing))
for f in [min, max]
for arg in ["", "a", 1, -1.0, [2]]
@test ismissing(f(missing, arg))
@test ismissing(f(arg, missing))
end
end
end
@testset "two-argument functions" begin
two_argument_functions = [atan, hypot, log]
# All two-argument functions return missing when operating on two missing's
# All two-argument functions return missing when operating on a scalar and an missing
# All two-argument functions return missing when operating on an missing and a scalar
for f in two_argument_functions
@test ismissing(f(missing, missing))
@test ismissing(f(1, missing))
@test ismissing(f(missing, 1))
end
end
@testset "bit operators" begin
bit_operators = [&, |, ⊻]
# All bit operators return missing when operating on two missing's
for f in bit_operators
@test ismissing(f(missing, missing))
end
end
@testset "boolean operators" begin
@test ismissing(missing & true)
@test ismissing(true & missing)
@test !(missing & false)
@test !(false & missing)
@test ismissing(missing | false)
@test ismissing(false | missing)
@test missing | true
@test true | missing
@test ismissing(xor(missing, true))
@test ismissing(xor(true, missing))
@test ismissing(xor(missing, false))
@test ismissing(xor(false, missing))
@test ismissing(nand(missing, true))
@test ismissing(nand(true, missing))
@test nand(missing, false) == true
@test nand(false, missing) == true
@test ismissing(⊼(missing, true))
@test ismissing(⊼(true, missing))
@test ⊼(missing, false) == true
@test ⊼(false, missing) == true
@test nor(missing, true) == false
@test nor(true, missing) == false
@test ismissing(nor(missing, false))
@test ismissing(nor(false, missing))
@test ⊽(missing, true) == false
@test ⊽(true, missing) == false
@test ismissing(⊽(missing, false))
@test ismissing(⊽(false, missing))
@test ismissing(missing & 1)
@test ismissing(1 & missing)
@test ismissing(missing | 1)
@test ismissing(1 | missing)
@test ismissing(xor(missing, 1))
@test ismissing(xor(1, missing))
@test ismissing(nand(missing, 1))
@test ismissing(nand(1, missing))
@test ismissing(⊼(missing, 1))
@test ismissing(⊼(1, missing))
@test ismissing(nor(missing, 1))
@test ismissing(nor(1, missing))
@test ismissing(⊽(missing, 1))
@test ismissing(⊽(1, missing))
end
@testset "* string/char concatenation" begin
@test ismissing("a" * missing)
@test ismissing('a' * missing)
@test ismissing(missing * "a")
@test ismissing(missing * 'a')
end
# Emulate a unitful type such as Dates.Minute
struct Unit
value::Int
end
Base.zero(::Type{Unit}) = Unit(0)
Base.one(::Type{Unit}) = 1
@testset "elementary functions" begin
elementary_functions = [abs, abs2, sign, real, imag,
acos, acosh, asin, asinh, atan, atanh, sin, sinh,
conj, cos, cosh, tan, tanh,
exp, exp2, expm1, log, log10, log1p, log2,
exponent, sqrt,
identity, zero, one, oneunit,
iseven, isodd, ispow2,
isfinite, isinf, isnan, iszero,
isinteger, isreal, transpose, adjoint, float, complex, inv]
# All elementary functions return missing when evaluating missing
for f in elementary_functions
@test ismissing(f(missing))
end
@test ismissing(clamp(missing, 1, 2))
for T in (Int, Float64)
@test zero(Union{T, Missing}) === T(0)
@test one(Union{T, Missing}) === T(1)
@test oneunit(Union{T, Missing}) === T(1)
@test float(Union{T, Missing}) === Union{float(T), Missing}
@test complex(Union{T, Missing}) === Union{complex(T), Missing}
end
@test_throws MethodError zero(Union{Symbol, Missing})
@test_throws MethodError one(Union{Symbol, Missing})
@test_throws MethodError oneunit(Union{Symbol, Missing})
@test_throws MethodError float(Union{Symbol, Missing})
@test_throws MethodError complex(Union{Symbol, Missing})
for T in (Unit,)
@test zero(Union{T, Missing}) === T(0)
@test one(Union{T, Missing}) === 1
@test oneunit(Union{T, Missing}) === T(1)
end
@test zero(Missing) === missing
@test one(Missing) === missing
@test oneunit(Missing) === missing
@test float(Missing) === Missing
@test complex(Missing) === Missing
@test_throws MethodError zero(Any)
@test_throws MethodError one(Any)
@test_throws MethodError oneunit(Any)
@test_throws MethodError float(Any)
@test_throws MethodError complex(Any)
@test_throws MethodError zero(String)
@test_throws MethodError zero(Union{String, Missing})
end
@testset "rounding functions" begin
# All rounding functions return missing when evaluating missing as first argument
# Check that the RoundingMode argument is passed on correctly
@test round(Union{Int, Missing}, 0.9) === round(Int, 0.9)
@test round(Union{Int, Missing}, 0.9, RoundToZero) === round(Int, 0.9, RoundToZero)
# Test elementwise on mixed arrays to ensure signature of Missing methods matches that of Float methods
test_array = [1.0, missing]
@test isequal(round.(test_array, RoundNearest), test_array)
@test isequal(round.(Union{Int, Missing}, test_array, RoundNearest), test_array)
rounding_functions = [ceil, floor, round, trunc]
for f in rounding_functions
@test_throws MissingException f(Int, missing)
@test isequal(f.(test_array), test_array)
@test isequal(f.(test_array, digits=0, base=10), test_array)
@test isequal(f.(test_array, sigdigits=1, base=10), test_array)
@test isequal(f.(Union{Int, Missing}, test_array), test_array)
end
end
@testset "printing" begin
@test sprint(show, missing) == "missing"
@test sprint(show, missing, context=:compact => true) == "missing"
@test sprint(show, [missing]) == "[missing]"
@test sprint(show, [1 missing]) == "$(Union{Int, Missing})[1 missing]"
b = IOBuffer()
display(TextDisplay(b), [missing])
@test String(take!(b)) == "1-element Vector{$Missing}:\n missing\n"
b = IOBuffer()
display(TextDisplay(b), [1 missing])
@test String(take!(b)) == "1×2 Matrix{$(Union{Int, Missing})}:\n 1 missing\n"
end
@testset "arrays with missing values" begin
x = convert(Vector{Union{Int, Missing}}, [1.0, missing])
@test isa(x, Vector{Union{Int, Missing}})
@test isequal(x, [1, missing])
x = convert(Vector{Union{Int, Missing}}, [1.0])
@test isa(x, Vector{Union{Int, Missing}})
@test x == [1]
x = convert(Vector{Union{Int, Missing}}, [missing])
@test isa(x, Vector{Union{Int, Missing}})
@test isequal(x, [missing])
@test eltype(adjoint([1, missing])) == Union{Int, Missing}
# issue #32777
let a = [0, nothing, 0.0, missing]
@test a[1] === 0.0
@test a[2] === nothing
@test a[3] === 0.0
@test a[4] === missing
@test a isa Vector{Union{Missing, Nothing, Float64}}
end
end
@testset "== and != on arrays" begin
@test ismissing([1, missing] == [1, missing])
@test ismissing(["a", missing] == ["a", missing])
@test ismissing(Any[1, missing] == Any[1, missing])
@test ismissing(Any[missing] == Any[missing])
@test ismissing([missing] == [missing])
@test ismissing(Any[missing, 2] == Any[1, missing])
@test ismissing([missing, false] == BitArray([true, false]))
@test ismissing(Any[missing, false] == BitArray([true, false]))
@test Union{Int, Missing}[1] == Union{Float64, Missing}[1.0]
@test Union{Int, Missing}[1] == [1.0]
@test Union{Bool, Missing}[true] == BitArray([true])
@test !([missing, 1] == [missing, 2])
@test !(Union{Int, Missing}[1] == [2])
@test !([1] == Union{Int, Missing}[2])
@test !(Union{Int, Missing}[1] == Union{Int, Missing}[2])
@test ismissing([1, missing] != [1, missing])
@test ismissing(["a", missing] != ["a", missing])
@test ismissing(Any[1, missing] != Any[1, missing])
@test ismissing(Any[missing] != Any[missing])
@test ismissing([missing] != [missing])
@test ismissing(Any[missing, 2] != Any[1, missing])
@test ismissing([missing, false] != BitArray([true, false]))
@test ismissing(Any[missing, false] != BitArray([true, false]))
@test !(Union{Int, Missing}[1] != Union{Float64, Missing}[1.0])
@test !(Union{Int, Missing}[1] != [1.0])
@test !(Union{Bool, Missing}[true] != BitArray([true]))
@test [missing, 1] != [missing, 2]
@test Union{Int, Missing}[1] != [2]
@test [1] != Union{Int, Missing}[2]
@test Union{Int, Missing}[1] != Union{Int, Missing}[2]
end
@testset "== and != on tuples" begin
@test ismissing((1, missing) == (1, missing))
@test ismissing(("a", missing) == ("a", missing))
@test ismissing((missing,) == (missing,))
@test ismissing((missing, 2) == (1, missing))
@test !((missing, 1) == (missing, 2))
longtuple = (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16)
@test ismissing((longtuple...,17,missing) == (longtuple...,17,18))
@test ismissing((longtuple...,missing,18) == (longtuple...,17,18))
@test !((longtuple...,17,missing) == (longtuple...,-17,18))
@test !((longtuple...,missing,18) == (longtuple...,17,-18))
@test ismissing((1, missing) != (1, missing))
@test ismissing(("a", missing) != ("a", missing))
@test ismissing((missing,) != (missing,))
@test ismissing((missing, 2) != (1, missing))
@test (missing, 1) != (missing, 2)
@test ismissing((longtuple...,17,missing) != (longtuple...,17,18))
@test ismissing((longtuple...,missing,18) != (longtuple...,17,18))
@test (longtuple...,17,missing) != (longtuple...,-17,18)
@test (longtuple...,missing,18) != (longtuple...,17,-18)
end
@testset "< and isless on tuples" begin
@test ismissing((1, missing) < (1, 3))
@test ismissing((1, missing) < (1, missing))
@test ismissing((missing, 1) < (missing, 2))
@test ismissing((1, 2) < (1, missing))
@test ismissing((1, missing) < (1, 2))
@test ismissing((missing,) < (missing,))
@test ismissing((1,) < (missing,))
@test () < (missing,)
@test (1,) < (2, missing)
@test (1, missing,) < (2, missing)
@test !isless((1, missing), (1, 3))
@test !isless((1, missing), (1, missing))
@test isless((missing, 1), (missing, 2))
@test isless((1, 2), (1, missing))
@test !isless((1, missing), (1, 2))
@test !isless((missing,), (missing,))
@test isless((1,), (missing,))
@test isless((), (missing,))
@test isless((1,), (2, missing))
@test isless((1, missing,), (2, missing))
end
@testset "any & all" begin
@test any([true, missing])
@test any(x -> x == 1, [1, missing])
@test ismissing(any([false, missing]))
@test ismissing(any(x -> x == 1, [2, missing]))
@test ismissing(all([true, missing]))
@test ismissing(all(x -> x == 1, [1, missing]))
@test !all([false, missing])
@test !all(x -> x == 1, [2, missing])
@test 1 in [1, missing]
@test ismissing(2 in [1, missing])
@test ismissing(missing in [1, missing])
end
@testset "float" begin
@test isequal(float([1, missing]), [1, missing])
@test float([1, missing]) isa Vector{Union{Float64, Missing}}
@test isequal(float(Union{Int, Missing}[missing]), [missing])
@test float(Union{Int, Missing}[missing]) isa Vector{Union{Float64, Missing}}
@test float(Union{Int, Missing}[1]) == [1]
@test float(Union{Int, Missing}[1]) isa Vector{Union{Float64, Missing}}
@test isequal(float([missing]), [missing])
@test float([missing]) isa Vector{Missing}
end
@testset "skipmissing" begin
x = skipmissing([1, 2, missing, 4])
@test eltype(x) === Int
@test collect(x) == [1, 2, 4]
@test collect(x) isa Vector{Int}
x = skipmissing([1 2; missing 4])
@test eltype(x) === Int
@test collect(x) == [1, 2, 4]
@test collect(x) isa Vector{Int}
x = collect(skipmissing([missing]))
@test eltype(x) === Union{}
@test isempty(collect(x))
@test collect(x) isa Vector{Union{}}
x = collect(skipmissing(Union{Int, Missing}[]))
@test eltype(x) === Int
@test isempty(collect(x))
@test collect(x) isa Vector{Int}
x = skipmissing([missing, missing, 1, 2, missing, 4, missing, missing])
@test eltype(x) === Int
@test collect(x) == [1, 2, 4]
@test collect(x) isa Vector{Int}
x = skipmissing(v for v in [missing, 1, missing, 2, 4])
@test eltype(x) === Any
@test collect(x) == [1, 2, 4]
@test collect(x) isa Vector{Int}
@testset "indexing" begin
x = skipmissing([1, missing, 2, missing, missing])
@test collect(eachindex(x)) == collect(keys(x)) == [1, 3]
@test x[1] === 1
@test x[3] === 2
@test_throws MissingException x[2]
@test_throws BoundsError x[6]
@test findfirst(==(2), x) == 3
@test findall(==(2), x) == [3]
@test argmin(x) == 1
@test findmin(x) == (1, 1)
@test argmax(x) == 3
@test findmax(x) == (2, 3)
x = skipmissing([missing 2; 1 missing])
@test collect(eachindex(x)) == [2, 3]
@test collect(keys(x)) == [CartesianIndex(2, 1), CartesianIndex(1, 2)]
@test x[2] === x[2, 1] === 1
@test x[3] === x[1, 2] === 2
@test_throws MissingException x[1]
@test_throws MissingException x[1, 1]
@test_throws BoundsError x[5]
@test_throws BoundsError x[3, 1]
@test findfirst(==(2), x) == CartesianIndex(1, 2)
@test findall(==(2), x) == [CartesianIndex(1, 2)]
@test argmin(x) == CartesianIndex(2, 1)
@test findmin(x) == (1, CartesianIndex(2, 1))
@test argmax(x) == CartesianIndex(1, 2)
@test findmax(x) == (2, CartesianIndex(1, 2))
for x in (skipmissing([]), skipmissing([missing, missing]))
@test isempty(collect(eachindex(x)))
@test isempty(collect(keys(x)))
@test_throws BoundsError x[3]
@test_throws BoundsError x[3, 1]
@test findfirst(==(2), x) === nothing
@test isempty(findall(==(2), x))
@test_throws "reducing over an empty collection is not allowed" argmin(x)
@test_throws "reducing over an empty collection is not allowed" findmin(x)
@test_throws "reducing over an empty collection is not allowed" argmax(x)
@test_throws "reducing over an empty collection is not allowed" findmax(x)
end
end
@testset "mapreduce" begin
# Vary size to test splitting blocks with several configurations of missing values
for T in (Int, Float64),
A in (rand(T, 10), rand(T, 1000), rand(T, 10000))
if T === Int
@test sum(A) === @inferred(sum(skipmissing(A))) ===
@inferred(reduce(+, skipmissing(A))) ===
@inferred(mapreduce(identity, +, skipmissing(A)))
else
@test sum(A) ≈ @inferred(sum(skipmissing(A))) ===
@inferred(reduce(+, skipmissing(A))) ===
@inferred(mapreduce(identity, +, skipmissing(A)))
end
@test mapreduce(cos, *, A) ≈
@inferred(mapreduce(cos, *, skipmissing(A)))
B = Vector{Union{T,Missing}}(A)
replace!(x -> rand(Bool) ? x : missing, B)
if T === Int
@test sum(collect(skipmissing(B))) ===
@inferred(sum(skipmissing(B))) ===
@inferred(reduce(+, skipmissing(B))) ===
@inferred(mapreduce(identity, +, skipmissing(B)))
else
@test sum(collect(skipmissing(B))) ≈ @inferred(sum(skipmissing(B))) ===
@inferred(reduce(+, skipmissing(B))) ===
@inferred(mapreduce(identity, +, skipmissing(B)))
end
@test mapreduce(cos, *, collect(skipmissing(A))) ≈
@inferred(mapreduce(cos, *, skipmissing(A)))
# Test block full of missing values
B[1:length(B)÷2] .= missing
if T === Int
@test sum(collect(skipmissing(B))) == sum(skipmissing(B)) ==
reduce(+, skipmissing(B)) == mapreduce(identity, +, skipmissing(B))
else
@test sum(collect(skipmissing(B))) ≈ sum(skipmissing(B)) ==
reduce(+, skipmissing(B)) == mapreduce(identity, +, skipmissing(B))
end
@test mapreduce(cos, *, collect(skipmissing(A))) ≈ mapreduce(cos, *, skipmissing(A))
end
# Patterns that exercize code paths for inputs with 1 or 2 non-missing values
@test sum(skipmissing([1, missing, missing, missing])) === 1
@test sum(skipmissing([missing, missing, missing, 1])) === 1
@test sum(skipmissing([1, missing, missing, missing, 2])) === 3
@test sum(skipmissing([missing, missing, missing, 1, 2])) === 3
for n in 0:3
itr = skipmissing(Vector{Union{Int,Missing}}(fill(missing, n)))
@test sum(itr) == reduce(+, itr) == mapreduce(identity, +, itr) === 0
@test_throws "reducing over an empty collection is not allowed" reduce(x -> x/2, itr)
@test_throws "reducing over an empty collection is not allowed" mapreduce(x -> x/2, +, itr)
end
# issue #35504
nt = NamedTuple{(:x, :y),Tuple{Union{Missing, Int},Union{Missing, Float64}}}(
(missing, missing))
@test sum(skipmissing(nt)) === 0
# issues #38627 and #124
@testset for len in [1, 2, 15, 16, 1024, 1025]
v = repeat(Union{Int,Missing}[1], len)
oa = OffsetArray(v, typemax(Int)-length(v))
sm = skipmissing(oa)
@test sum(sm) == len
v = repeat(Union{Int,Missing}[missing], len)
oa = OffsetArray(v, typemax(Int)-length(v))
sm = skipmissing(oa)
@test sum(sm) == 0
end
end
@testset "filter" begin
allmiss = Vector{Union{Int,Missing}}(missing, 10)
@test isempty(filter(isodd, skipmissing(allmiss))::Vector{Int})
twod1 = [1.0f0 missing; 3.0f0 missing]
@test filter(x->x > 0, skipmissing(twod1))::Vector{Float32} == [1, 3]
twod2 = [1.0f0 2.0f0; 3.0f0 4.0f0]
@test filter(x->x > 0, skipmissing(twod2)) == reshape(twod2, (4,))
end
end
@testset "coalesce" begin
@test coalesce() === missing
@test coalesce(1) === 1
@test coalesce(nothing) === nothing
@test coalesce(missing) === missing
@test coalesce(missing, 1) === 1
@test coalesce(1, missing) === 1
@test coalesce(missing, missing) === missing
@test coalesce(missing, 1, 2) === 1
@test coalesce(1, missing, 2) === 1
@test coalesce(missing, missing, 2) === 2
@test coalesce(missing, missing, missing) === missing
@test coalesce(nothing, missing) === nothing
@test coalesce(missing, nothing) === nothing
end
@testset "@coalesce" begin
@test @coalesce() === missing
@test @coalesce(1) === 1
@test @coalesce(nothing) === nothing
@test @coalesce(missing) === missing
@test @coalesce(1, error("failed")) === 1
@test_throws ErrorException @coalesce(missing, error("failed"))
end
mutable struct Obj; x; end
@testset "weak references" begin
@noinline function mk_wr(r, wr)
x = Obj(1)
push!(r, x)
push!(wr, WeakRef(x))
nothing
end
ref = []
wref = []
mk_wr(ref, wref)
@test ismissing(wref[1] == missing)
@test ismissing(missing == wref[1])
end
@testset "showerror missing function" begin
me = try missing(1) catch e e end
@test sprint(showerror, me) == "MethodError: objects of type Missing are not callable"
end
@testset "sort and sortperm with $(eltype(X))" for (X, P, RP) in
(([2, missing, -2, 5, missing], [3, 1, 4, 2, 5], [2, 5, 4, 1, 3]),
([NaN, missing, 5, -0.0, NaN, missing, Inf, 0.0, -Inf],
[9, 4, 8, 3, 7, 1, 5, 2, 6], [2, 6, 1, 5, 7, 3, 8, 4, 9]),
([missing, "a", "c", missing, "b"], [2, 5, 3, 1, 4], [1, 4, 3, 5, 2]))
@test sortperm(X) == P
@test sortperm(X, alg=QuickSort) == P
@test sortperm(X, alg=MergeSort) == P
XP = X[P]
@test isequal(sort(X), XP)
@test isequal(sort(X, alg=QuickSort), XP)
@test isequal(sort(X, alg=MergeSort), XP)
@test sortperm(X, rev=true) == RP
@test sortperm(X, alg=QuickSort, rev=true) == RP
@test sortperm(X, alg=MergeSort, rev=true) == RP
XRP = X[RP]
@test isequal(sort(X, rev=true), XRP)
@test isequal(sort(X, alg=QuickSort, rev=true), XRP)
@test isequal(sort(X, alg=MergeSort, rev=true), XRP)
end
sortperm(reverse([NaN, missing, NaN, missing]))