Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions src/algebra.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,14 @@
-(::InfiniteCardinal) = -∞


# The direction of an infinity in half turns, which for a real is its sign bit.
@inline _halfturns(x) = signbit(x)
@inline _halfturns(x::Complex) = angle(x)/π


# addition
@inline toinf(x) = RealInfinity(signbit(x))
@inline toinf(x::Complex) = ComplexInfinity(angle(x))
@inline toinf(x::Complex) = ComplexInfinity(_halfturns(x))
@inline toinf(x::ComplexInfinity) = x

@inline _infadd(x, y) = angle(x) == angle(y) ? y : throw(ArgumentError("Angles must be the same to add ∞"))
Expand All @@ -44,13 +49,10 @@

# multiplication

@inline _sb(x) = signbit(x)
@inline _sb(x::Complex) = angle(x)/π # overloading `signbit` causes type piracy

@inline __mul(x, y::AllInfinities) = RealInfinity(_sb(x) ⊻ _sb(y))
@inline __mul(x, y::ComplexInfinity) = ComplexInfinity(_sb(x) + _sb(y))
@inline __mul(x, y::ComplexInfinity{Bool}) = ComplexInfinity(_sb(x) ⊻ _sb(y))
@inline __mul(x::Complex, y::ComplexInfinity{Bool}) = ComplexInfinity(_sb(x) + _sb(y))
@inline __mul(x, y::AllInfinities) = RealInfinity(_halfturns(x) ⊻ _halfturns(y))
@inline __mul(x, y::ComplexInfinity) = ComplexInfinity(_halfturns(x) + _halfturns(y))
@inline __mul(x, y::ComplexInfinity{Bool}) = ComplexInfinity(_halfturns(x) ⊻ _halfturns(y))
@inline __mul(x::Complex, y::ComplexInfinity{Bool}) = ComplexInfinity(_halfturns(x) + _halfturns(y))
@inline __mul(x::Integer, y::InfiniteCardinal) = x > 0 ? y : throw(ArgumentError("Cannot multiply $x * $y"))

@inline _mul(x, y) = iszero(x) ? throw(ArgumentError("Cannot multiply $x * $y")) : __mul(infpromote(x, y)...)
Expand Down
26 changes: 25 additions & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -283,12 +283,36 @@ Base.iterate(s::CharString, i::Integer=1) = i ≤ length(s.chars) ? (s.chars[i],
ComplexInfinity() * ∞ ≡ ComplexInfinity() * RealInfinity() ≡ ComplexInfinity()

@test 2.0im*∞ ≡ ∞*2.0im ≡ 2.0im * RealInfinity() ≡ RealInfinity() * 2.0im ≡ ComplexInfinity(1/2)
# On an axis `angle(x)/π` is exact, `angle` giving exactly `0`, `±π/2` or `π` and halving being exact.
for (z, s, w) in ((2.0+0.0im, 0.0, complex(Inf, 0.0)), (2.0im, 0.5, complex(0.0, Inf)),
(-2.0+0.0im, 1.0, complex(-Inf, 0.0)), (-2.0im, -0.5, complex(0.0, -Inf)))
@test z*∞ ≡ ComplexInfinity(s)
@test z*∞ == w
end
@test 2ComplexInfinity() ≡ ComplexInfinity()*2 ≡ ComplexInfinity()

@test exp(im*π/4)*∞ == Inf+im*Inf
# Off the axes it is not exact in general: `angle(exp(im*π/8))/π` gives 0.12500000000000003.
for θ in (π/8, π/4, 0.3, -2.4)
@test angle(exp(im*θ)*∞) ≈ angle(∞*exp(im*θ)) ≈ θ
end
# The direction is the one the operand actually has, so rescaling keeps it only while it is exact.
@test 4*(0.3+0.1im)*∞ ≡ (0.3+0.1im)*∞
@test 3*(0.3+0.1im)*∞ ≢ (0.3+0.1im)*∞
@test exp(im*π/4)+∞ == ∞
@test Inf + im + ∞ ≡ ComplexInfinity()

# An infinite summand reaches `_infadd` through `toinf`, which has to answer in half turns.
@test complex(Inf, 0.0) + ∞ ≡ ComplexInfinity()
@test complex(-Inf, 0.0) + (-∞) ≡ -ComplexInfinity()
@test complex(0.0, Inf) + ComplexInfinity(1/2) ≡ ComplexInfinity(1/2)
@test complex(0.0, -Inf) + ComplexInfinity(-1/2) ≡ ComplexInfinity(-1/2)
@test_throws ArgumentError complex(0.0, Inf) + ∞

# Two infinite parts are the only way an infinite `Complex` points off the axes.
for (x, y, s) in ((Inf, Inf, 1/4), (-Inf, Inf, 3/4), (-Inf, -Inf, -3/4), (Inf, -Inf, -1/4))
@test complex(x, y) + ComplexInfinity(s) ≡ ComplexInfinity(s)
end

@test Inf == ComplexInfinity()
@test ComplexInfinity() == Inf

Expand Down
Loading