Skip to content

Commit

Permalink
Add type NegInf representing negative infinity (#1528)
Browse files Browse the repository at this point in the history
Also expand the functionality of `PosInf` to match.
  • Loading branch information
fingolfin authored Sep 8, 2023
1 parent 257423f commit 5781497
Show file tree
Hide file tree
Showing 9 changed files with 226 additions and 81 deletions.
118 changes: 92 additions & 26 deletions src/HeckeMiscInfinity.jl
Original file line number Diff line number Diff line change
@@ -1,61 +1,127 @@
export PosInf, inf, IntExt, is_infinite
export PosInf, NegInf, inf, IntExt, is_infinite

# This is a type for positive infinity for use in valuations.
"""
PosInf
This singleton type represents positive infinity, as in: a value larger
than any real number. For use in valuations and elsewhere.
See [`NegInf`](@ref).
"""
struct PosInf
end

const inf = PosInf()
"""
NegInf
+(::Int, ::PosInf) = inf
This singleton type represents negative infinity, as in: a value smaller
than any real number. For use in valuations and elsewhere.
+(::PosInf, ::Int) = inf
See [`PosInf`](@ref).
"""
struct NegInf
end

+(::PosInf, ::PosInf) = inf
# type union for convenience later on
const AnyInf = Union{PosInf,NegInf}

-(::PosInf, ::Int) = inf
# another convenience type union
# TODO: maybe deprecate this one, or at least rename it; the current one seems
# somewhat arbitrary now that we also have negative infinity
const IntExt = Union{Int,PosInf}

Base.max(::Int, ::PosInf) = inf
const inf = PosInf() # TODO: for backwards compatibility; deprecate?

Base.max(::PosInf, ::Int) = inf
#const infinity = PosInf() # FIXME: can't have this as we already have `infinity(C::CalciumField)`

Base.isless(::Int, ::PosInf) = true
########################################
#
# basics
#
########################################

Base.isless(x::Rational{Int}, ::PosInf) = denominator(x) != 0
# match the hash values of Inf and -Inf, as we also compare equal to them
Base.hash(::PosInf, h::UInt) = hash(Inf, h)
Base.hash(::NegInf, h::UInt) = hash(-Inf, h)

Base.isless(::PosInf, ::PosInf) = false
Base.show(io::IO, ::PosInf) = print(io, "infinity") # FIXME: if we can't have `infinity` as a global, maybe better print as `inf`???
Base.show(io::IO, ::NegInf) = print(io, "-infinity")

Base.isless(::PosInf, ::Int) = false
Base.one(::AnyInf) = 1
Base.zero(::AnyInf) = 0

Base.isless(::PosInf, ::Rational{Int}) = false
########################################

Base.isfinite(::PosInf) = false
Base.signbit(::PosInf) = false
Base.signbit(::NegInf) = true

Base.isinf(::PosInf) = true
########################################
#
# comparison
#
########################################

Base.isone(::PosInf) = false
Base.:(==)(inf1::AnyInf, inf2::AnyInf) = signbit(inf1) == signbit(inf2)
Base.:(==)(x::AnyInf, y::Real) = isinf(y) && signbit(y) == signbit(x)
Base.:(==)(y::Real, x::AnyInf) = x == y

Base.iszero(::PosInf) = false

Base.one(::PosInf) = 1
Base.isless(x::Real, ::PosInf) = isfinite(x) || signbit(x)
Base.isless(::PosInf, ::Real) = false

Base.zero(::PosInf) = 0
Base.isless(::Real, ::NegInf) = false
Base.isless(::NegInf, x::Real) = isfinite(x) || !signbit(x)

Base.isless(::PosInf, ::ZZRingElem) = false
Base.isless(inf1::AnyInf, inf2::AnyInf) = signbit(inf1) && !signbit(inf2)

Base.isless(::ZZRingElem, ::PosInf) = true

Base.isless(::PosInf, ::QQFieldElem) = false
Base.isless(::PosInf, ::Union{ZZRingElem,QQFieldElem}) = false
Base.isless(::Union{ZZRingElem,QQFieldElem}, ::PosInf) = true

Base.isless(::QQFieldElem, ::PosInf) = true
Base.isless(::NegInf, ::Union{ZZRingElem,QQFieldElem}) = true
Base.isless(::Union{ZZRingElem,QQFieldElem}, ::NegInf) = false

const IntExt = Union{Int,PosInf}
########################################
#
# other predicates
#
########################################

Base.isfinite(::AnyInf) = false
Base.isinf(::AnyInf) = true

Base.isone(::AnyInf) = false
Base.iszero(::AnyInf) = false

is_positive(::PosInf) = true
is_positive(::NegInf) = false

is_negative(::PosInf) = false
is_negative(::NegInf) = true

@doc raw"""
is_infinite(x::Any) -> Bool
Tests whether $x$ is infinite, by returning `!isfinite(x)`.
Test whether $x$ is infinite.
"""
is_infinite(x::Any) = !isfinite(x)
# TODO: should is_infinite become a synonym for `isinf` ???

########################################
#
# arithmetic
#
########################################

# unary minus
Base.:-(::PosInf) = NegInf()
Base.:-(::NegInf) = inf

# binary operations
Base.:+(::IntegerUnion, inf::AnyInf) = inf
Base.:+(inf::AnyInf, ::IntegerUnion) = inf
Base.:+(inf1::AnyInf, inf2::AnyInf) = signbit(inf1) == signbit(inf2) ? inf1 : error("inf - inf is undefined")

Base.:-(inf::AnyInf, ::IntegerUnion) = inf
Base.:-(::IntegerUnion, inf::AnyInf) = -inf
Base.:-(inf1::AnyInf, inf2::AnyInf) = inf1 + (-inf2)
9 changes: 0 additions & 9 deletions src/HeckeMiscInteger.jl
Original file line number Diff line number Diff line change
Expand Up @@ -253,15 +253,6 @@ function nbits(a::Integer)
return ndigits(a, base=2)
end

@doc raw"""
isinteger(a::QQFieldElem) -> Bool
Returns `true` iff the denominator of $a$ is one.
"""
function isinteger(a::QQFieldElem)
return isone(denominator(a))
end

function (::ZZRing)(x::Rational{Int})
@assert denominator(x) == 1
return ZZRingElem(numerator(x))
Expand Down
2 changes: 2 additions & 0 deletions src/arb/arb.jl
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,8 @@ function sign(::Type{Int}, x::arb)
end
end

Base.signbit(x::arb) = signbit(sign(Int, x))

################################################################################
#
# Unary operations
Expand Down
9 changes: 9 additions & 0 deletions src/flint/fmpq.jl
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ sign(a::QQFieldElem) = QQFieldElem(sign(numerator(a)))

sign(::Type{Int}, a::QQFieldElem) = sign(Int, numerator(a))

Base.signbit(a::QQFieldElem) = signbit(sign(Int, a))

function abs(a::QQFieldElem)
z = QQFieldElem()
ccall((:fmpq_abs, libflint), Nothing, (Ref{QQFieldElem}, Ref{QQFieldElem}), z, a)
Expand All @@ -124,6 +126,13 @@ end

is_unit(a::QQFieldElem) = !iszero(a)

isinteger(a::QQFieldElem) = isone(denominator(a))

isfinite(::QQFieldElem) = true

isinf(::QQFieldElem) = false


@doc raw"""
height(a::QQFieldElem)
Expand Down
4 changes: 4 additions & 0 deletions src/flint/fmpz.jl
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,8 @@ sign(a::ZZRingElem) = ZZRingElem(ccall((:fmpz_sgn, libflint), Cint, (Ref{ZZRingE

sign(::Type{Int}, a::ZZRingElem) = Int(ccall((:fmpz_sgn, libflint), Cint, (Ref{ZZRingElem},), a))

Base.signbit(a::ZZRingElem) = signbit(sign(Int, a))

@doc raw"""
fits(::Type{Int}, a::ZZRingElem)
Expand Down Expand Up @@ -218,6 +220,8 @@ isinteger(::ZZRingElem) = true

isfinite(::ZZRingElem) = true

isinf(::ZZRingElem) = false

@doc raw"""
denominator(a::ZZRingElem)
Expand Down
102 changes: 65 additions & 37 deletions test/HeckeMiscInfinity-test.jl
Original file line number Diff line number Diff line change
@@ -1,57 +1,85 @@
@testset "PosInf basics" begin
@testset "Inf basics" begin

@test inf === PosInf()
@test -inf === NegInf()
@test -(-inf) === inf

@test zero(inf) === 0
@test one(inf) === 1

end

@testset "PosInf arithmetic" begin
@test zero(-inf) === 0
@test one(-inf) === 1

@test inf + inf === inf
@test inf + 1 === inf
@test inf - 1 === inf
@test 1 + inf === inf
@test !signbit(inf)
@test signbit(-inf)

end

@testset "PosInf comparisons" begin

@test max(inf, inf) === inf
@test max(inf, 1) === inf
@test max(1, inf) === inf

@test 1 < inf
@test !(inf < 1)
@testset "Inf arithmetic" begin

@test ZZ(1) < inf
@test !(inf < ZZ(1))

@test 1 // 2 < inf
@test !(inf < 1 // 2)

@test QQ(1 // 2) < inf
@test !(inf < QQ(1 // 2))

# one positive infinity is not less than infinity (though that does
# not necessarily mean that they are equal either)
@test !(inf < inf)
@test inf + inf === inf
@test inf - -inf === inf
@test -inf + -inf === -inf
@test -inf - inf === -inf

@test_throws ErrorException inf - inf
@test_throws ErrorException (-inf) - (-inf)

@testset "... with type $(typeof(x))" for x in [1, ZZ(1)]
@test inf + x === inf
@test x + inf === inf
@test inf - x === inf
@test x - inf === -inf

@test -inf + x === -inf
@test x + -inf === -inf
@test -inf - x === -inf
@test x - (-inf) === inf
end
end

@test !(inf < 1 // 0)
@test !(1 // 0 < inf)
@testset "Inf comparisons" begin

@testset "... against $x" for x in (1, ZZ(1), 1//2, QQ(1//2))
# we verify the following three objects are in the correct
# order with respect to isless and various other operations
ord = [ -inf, x, inf ]
for i in 1:length(ord), j in 1:length(ord)
@test min(ord[i], ord[j]) === ord[min(i, j)]
@test max(ord[i], ord[j]) === ord[max(i, j)]
@test isless(ord[i], ord[j]) === isless(i, j)
@test (ord[i] < ord[j]) === (i < j)
@test (ord[i] <= ord[j]) === (i <= j)
@test (ord[i] == ord[j]) === (i == j)
end
end

# verify that no positive infinity is less than any other
eqs = [ Inf, inf, 1//0 ]
for i in 1:length(eqs), j in 1:length(eqs)
@test !(eqs[i] < eqs[j])
end

# verify that no negative infinity is less than any other
eqs = [ -Inf, -inf, -1//0 ]
for i in 1:length(eqs), j in 1:length(eqs)
@test !(eqs[i] < eqs[j])
end

end

@testset "PosInf predicates" begin
@testset "Inf predicates for $x" for x in [inf, -inf]

@test !isone(inf)
@test !isfinite(inf)
@test !iszero(inf)
@test !isone(x)
@test !isfinite(x)
@test !iszero(x)

@test isinf(inf)
@test is_infinite(inf)
@test is_positive(inf)
@test isinf(x)
@test is_infinite(x)
@test is_positive(x) == !signbit(x)
@test is_positive(x) == (x > 0)
@test is_negative(x) == signbit(x)
@test is_negative(x) == (x < 0)

end

Expand Down
6 changes: 6 additions & 0 deletions test/arb/arb-test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,12 @@ end
b, i = unique_integer(RRR(2)^1000);
b, i = unique_integer(RRR(2)^1000);
b, i = unique_integer(RRR(2)^1000);

@test sign(Int, RR(2)) == 1
@test sign(Int, -RR(2)) == -1

@test !signbit(RR(2))
@test signbit(-RR(2))
end

@testset "arb.unsafe_ops" begin
Expand Down
20 changes: 19 additions & 1 deletion test/flint/fmpq-test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -132,13 +132,31 @@ end
@test sign(Int, QQFieldElem()) == 0
@test sign(Int, QQFieldElem(1, 7)) == 1

@test signbit(QQFieldElem(-2, 3))
@test !signbit(QQFieldElem())
@test !signbit(QQFieldElem(1, 7))

@test !isone(zero(R))
@test isone(one(R))

@test iszero(zero(R))
@test !iszero(one(R))

@test is_unit(one(R))

@test is_unit(QQFieldElem(1, 3))
@test !is_unit(QQFieldElem(0, 3))

@test isinteger(zero(R))
@test isinteger(one(R))
@test !isinteger(QQFieldElem(1, 3))

@test isfinite(zero(R))
@test isfinite(one(R))
@test isfinite(QQFieldElem(1, 3))

@test !isinf(zero(R))
@test !isinf(one(R))
@test !isinf(QQFieldElem(1, 3))

@test deepcopy(QQFieldElem(2, 3)) == QQFieldElem(2, 3)

Expand Down
Loading

0 comments on commit 5781497

Please sign in to comment.