915 lines
25 KiB
Julia
915 lines
25 KiB
Julia
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||
|
||
"""
|
||
Complex{T<:Real} <: Number
|
||
|
||
Complex number type with real and imaginary part of type `T`.
|
||
|
||
`Complex32`, `Complex64` and `Complex128` are aliases for
|
||
`Complex{Float16}`, `Complex{Float32}` and `Complex{Float64}` respectively.
|
||
"""
|
||
struct Complex{T<:Real} <: Number
|
||
re::T
|
||
im::T
|
||
end
|
||
Complex(x::Real, y::Real) = Complex(promote(x,y)...)
|
||
Complex(x::Real) = Complex(x, zero(x))
|
||
|
||
"""
|
||
im
|
||
|
||
The imaginary unit.
|
||
"""
|
||
const im = Complex(false,true)
|
||
|
||
const Complex128 = Complex{Float64}
|
||
const Complex64 = Complex{Float32}
|
||
const Complex32 = Complex{Float16}
|
||
|
||
convert(::Type{Complex{T}}, x::Real) where {T<:Real} = Complex{T}(x,0)
|
||
convert(::Type{Complex{T}}, z::Complex) where {T<:Real} = Complex{T}(real(z),imag(z))
|
||
convert(::Type{T}, z::Complex) where {T<:Real} =
|
||
isreal(z) ? convert(T,real(z)) : throw(InexactError())
|
||
|
||
convert(::Type{Complex}, z::Complex) = z
|
||
convert(::Type{Complex}, x::Real) = Complex(x)
|
||
|
||
promote_rule(::Type{Complex{T}}, ::Type{S}) where {T<:Real,S<:Real} =
|
||
Complex{promote_type(T,S)}
|
||
promote_rule(::Type{Complex{T}}, ::Type{Complex{S}}) where {T<:Real,S<:Real} =
|
||
Complex{promote_type(T,S)}
|
||
|
||
widen(::Type{Complex{T}}) where {T} = Complex{widen(T)}
|
||
|
||
"""
|
||
real(z)
|
||
|
||
Return the real part of the complex number `z`.
|
||
|
||
```jldoctest
|
||
julia> real(1 + 3im)
|
||
1
|
||
```
|
||
"""
|
||
real(z::Complex) = z.re
|
||
|
||
"""
|
||
imag(z)
|
||
|
||
Return the imaginary part of the complex number `z`.
|
||
|
||
```jldoctest
|
||
julia> imag(1 + 3im)
|
||
3
|
||
```
|
||
"""
|
||
imag(z::Complex) = z.im
|
||
real(x::Real) = x
|
||
imag(x::Real) = zero(x)
|
||
|
||
"""
|
||
reim(z)
|
||
|
||
Return both the real and imaginary parts of the complex number `z`.
|
||
|
||
```jldoctest
|
||
julia> reim(1 + 3im)
|
||
(1, 3)
|
||
```
|
||
"""
|
||
reim(z) = (real(z), imag(z))
|
||
|
||
"""
|
||
real(T::Type)
|
||
|
||
Returns the type that represents the real part of a value of type `T`.
|
||
e.g: for `T == Complex{R}`, returns `R`.
|
||
Equivalent to `typeof(real(zero(T)))`.
|
||
|
||
```jldoctest
|
||
julia> real(Complex{Int})
|
||
Int64
|
||
|
||
julia> real(Float64)
|
||
Float64
|
||
```
|
||
"""
|
||
real(T::Type) = typeof(real(zero(T)))
|
||
real(::Type{T}) where {T<:Real} = T
|
||
real(::Type{Complex{T}}) where {T<:Real} = T
|
||
|
||
"""
|
||
isreal(x) -> Bool
|
||
|
||
Test whether `x` or all its elements are numerically equal to some real number.
|
||
|
||
```jldoctest
|
||
julia> isreal(5.)
|
||
true
|
||
|
||
julia> isreal([4.; complex(0,1)])
|
||
false
|
||
```
|
||
"""
|
||
isreal(x::Real) = true
|
||
isreal(z::Complex) = iszero(imag(z))
|
||
isinteger(z::Complex) = isreal(z) & isinteger(real(z))
|
||
isfinite(z::Complex) = isfinite(real(z)) & isfinite(imag(z))
|
||
isnan(z::Complex) = isnan(real(z)) | isnan(imag(z))
|
||
isinf(z::Complex) = isinf(real(z)) | isinf(imag(z))
|
||
iszero(z::Complex) = iszero(real(z)) & iszero(imag(z))
|
||
|
||
"""
|
||
complex(r, [i])
|
||
|
||
Convert real numbers or arrays to complex. `i` defaults to zero.
|
||
"""
|
||
complex(z::Complex) = z
|
||
complex(x::Real) = Complex(x)
|
||
complex(x::Real, y::Real) = Complex(x, y)
|
||
|
||
"""
|
||
complex(T::Type)
|
||
|
||
Returns an appropriate type which can represent a value of type `T` as a complex number.
|
||
Equivalent to `typeof(complex(zero(T)))`.
|
||
|
||
```jldoctest
|
||
julia> complex(Complex{Int})
|
||
Complex{Int64}
|
||
|
||
julia> complex(Int)
|
||
Complex{Int64}
|
||
```
|
||
"""
|
||
complex(::Type{T}) where {T<:Real} = Complex{T}
|
||
complex(::Type{Complex{T}}) where {T<:Real} = Complex{T}
|
||
|
||
flipsign(x::Complex, y::Real) = ifelse(signbit(y), -x, x)
|
||
|
||
function show(io::IO, z::Complex)
|
||
r, i = reim(z)
|
||
compact = get(io, :compact, false)
|
||
show(io, r)
|
||
if signbit(i) && !isnan(i)
|
||
i = -i
|
||
print(io, compact ? "-" : " - ")
|
||
else
|
||
print(io, compact ? "+" : " + ")
|
||
end
|
||
show(io, i)
|
||
if !(isa(i,Integer) && !isa(i,Bool) || isa(i,AbstractFloat) && isfinite(i))
|
||
print(io, "*")
|
||
end
|
||
print(io, "im")
|
||
end
|
||
show(io::IO, z::Complex{Bool}) =
|
||
print(io, z == im ? "im" : "Complex($(z.re),$(z.im))")
|
||
|
||
function read(s::IO, ::Type{Complex{T}}) where T<:Real
|
||
r = read(s,T)
|
||
i = read(s,T)
|
||
Complex{T}(r,i)
|
||
end
|
||
function write(s::IO, z::Complex)
|
||
write(s,real(z),imag(z))
|
||
end
|
||
|
||
## byte order swaps: real and imaginary part are swapped individually
|
||
bswap(z::Complex) = Complex(bswap(real(z)), bswap(imag(z)))
|
||
|
||
## equality and hashing of complex numbers ##
|
||
|
||
==(z::Complex, w::Complex) = (real(z) == real(w)) & (imag(z) == imag(w))
|
||
==(z::Complex, x::Real) = isreal(z) && real(z) == x
|
||
==(x::Real, z::Complex) = isreal(z) && real(z) == x
|
||
|
||
isequal(z::Complex, w::Complex) = isequal(real(z),real(w)) & isequal(imag(z),imag(w))
|
||
|
||
if UInt === UInt64
|
||
const h_imag = 0x32a7a07f3e7cd1f9
|
||
else
|
||
const h_imag = 0x3e7cd1f9
|
||
end
|
||
const hash_0_imag = hash(0, h_imag)
|
||
|
||
function hash(z::Complex, h::UInt)
|
||
# TODO: with default argument specialization, this would be better:
|
||
# hash(real(z), h ⊻ hash(imag(z), h ⊻ h_imag) ⊻ hash(0, h ⊻ h_imag))
|
||
hash(real(z), h ⊻ hash(imag(z), h_imag) ⊻ hash_0_imag)
|
||
end
|
||
|
||
## generic functions of complex numbers ##
|
||
|
||
"""
|
||
conj(z)
|
||
|
||
Compute the complex conjugate of a complex number `z`.
|
||
|
||
```jldoctest
|
||
julia> conj(1 + 3im)
|
||
1 - 3im
|
||
```
|
||
"""
|
||
conj(z::Complex) = Complex(real(z),-imag(z))
|
||
abs(z::Complex) = hypot(real(z), imag(z))
|
||
abs2(z::Complex) = real(z)*real(z) + imag(z)*imag(z)
|
||
inv(z::Complex) = conj(z)/abs2(z)
|
||
inv(z::Complex{<:Integer}) = inv(float(z))
|
||
|
||
-(z::Complex) = Complex(-real(z), -imag(z))
|
||
+(z::Complex, w::Complex) = Complex(real(z) + real(w), imag(z) + imag(w))
|
||
-(z::Complex, w::Complex) = Complex(real(z) - real(w), imag(z) - imag(w))
|
||
*(z::Complex, w::Complex) = Complex(real(z) * real(w) - imag(z) * imag(w),
|
||
real(z) * imag(w) + imag(z) * real(w))
|
||
|
||
muladd(z::Complex, w::Complex, x::Complex) =
|
||
Complex(muladd(real(z), real(w), real(x)) - imag(z)*imag(w), # TODO: use mulsub given #15985
|
||
muladd(real(z), imag(w), muladd(imag(z), real(w), imag(x))))
|
||
|
||
# handle Bool and Complex{Bool}
|
||
# avoid type signature ambiguity warnings
|
||
+(x::Bool, z::Complex{Bool}) = Complex(x + real(z), imag(z))
|
||
+(z::Complex{Bool}, x::Bool) = Complex(real(z) + x, imag(z))
|
||
-(x::Bool, z::Complex{Bool}) = Complex(x - real(z), - imag(z))
|
||
-(z::Complex{Bool}, x::Bool) = Complex(real(z) - x, imag(z))
|
||
*(x::Bool, z::Complex{Bool}) = Complex(x * real(z), x * imag(z))
|
||
*(z::Complex{Bool}, x::Bool) = Complex(real(z) * x, imag(z) * x)
|
||
|
||
+(x::Bool, z::Complex) = Complex(x + real(z), imag(z))
|
||
+(z::Complex, x::Bool) = Complex(real(z) + x, imag(z))
|
||
-(x::Bool, z::Complex) = Complex(x - real(z), - imag(z))
|
||
-(z::Complex, x::Bool) = Complex(real(z) - x, imag(z))
|
||
*(x::Bool, z::Complex) = Complex(x * real(z), x * imag(z))
|
||
*(z::Complex, x::Bool) = Complex(real(z) * x, imag(z) * x)
|
||
|
||
+(x::Real, z::Complex{Bool}) = Complex(x + real(z), imag(z))
|
||
+(z::Complex{Bool}, x::Real) = Complex(real(z) + x, imag(z))
|
||
function -(x::Real, z::Complex{Bool})
|
||
# we don't want the default type for -(Bool)
|
||
re = x-real(z)
|
||
Complex(re, - oftype(re, imag(z)))
|
||
end
|
||
-(z::Complex{Bool}, x::Real) = Complex(real(z) - x, imag(z))
|
||
*(x::Real, z::Complex{Bool}) = Complex(x * real(z), x * imag(z))
|
||
*(z::Complex{Bool}, x::Real) = Complex(real(z) * x, imag(z) * x)
|
||
|
||
# adding or multiplying real & complex is common
|
||
+(x::Real, z::Complex) = Complex(x + real(z), imag(z))
|
||
+(z::Complex, x::Real) = Complex(x + real(z), imag(z))
|
||
function -(x::Real, z::Complex)
|
||
# we don't want the default type for -(Bool)
|
||
re = x - real(z)
|
||
Complex(re, - oftype(re, imag(z)))
|
||
end
|
||
-(z::Complex, x::Real) = Complex(real(z) - x, imag(z))
|
||
*(x::Real, z::Complex) = Complex(x * real(z), x * imag(z))
|
||
*(z::Complex, x::Real) = Complex(x * real(z), x * imag(z))
|
||
|
||
muladd(x::Real, z::Complex, y::Number) = muladd(z, x, y)
|
||
muladd(z::Complex, x::Real, y::Real) = Complex(muladd(real(z),x,y), imag(z)*x)
|
||
muladd(z::Complex, x::Real, w::Complex) =
|
||
Complex(muladd(real(z),x,real(w)), muladd(imag(z),x,imag(w)))
|
||
muladd(x::Real, y::Real, z::Complex) = Complex(muladd(x,y,real(z)), imag(z))
|
||
muladd(z::Complex, w::Complex, x::Real) =
|
||
Complex(muladd(real(z), real(w), x) - imag(z)*imag(w), # TODO: use mulsub given #15985
|
||
muladd(real(z), imag(w), imag(z) * real(w)))
|
||
|
||
/(a::R, z::S) where {R<:Real,S<:Complex} = (T = promote_type(R,S); a*inv(T(z)))
|
||
/(z::Complex, x::Real) = Complex(real(z)/x, imag(z)/x)
|
||
|
||
function /(a::Complex{T}, b::Complex{T}) where T<:Real
|
||
are = real(a); aim = imag(a); bre = real(b); bim = imag(b)
|
||
if abs(bre) <= abs(bim)
|
||
if isinf(bre) && isinf(bim)
|
||
r = sign(bre)/sign(bim)
|
||
else
|
||
r = bre / bim
|
||
end
|
||
den = bim + r*bre
|
||
Complex((are*r + aim)/den, (aim*r - are)/den)
|
||
else
|
||
if isinf(bre) && isinf(bim)
|
||
r = sign(bim)/sign(bre)
|
||
else
|
||
r = bim / bre
|
||
end
|
||
den = bre + r*bim
|
||
Complex((are + aim*r)/den, (aim - are*r)/den)
|
||
end
|
||
end
|
||
|
||
inv(z::Complex{<:Union{Float16,Float32}}) =
|
||
oftype(z, conj(widen(z))/abs2(widen(z)))
|
||
|
||
/(z::Complex{T}, w::Complex{T}) where {T<:Union{Float16,Float32}} =
|
||
oftype(z, widen(z)*inv(widen(w)))
|
||
|
||
# robust complex division for double precision
|
||
# the first step is to scale variables if appropriate ,then do calculations
|
||
# in a way that avoids over/underflow (subfuncs 1 and 2), then undo the scaling.
|
||
# scaling variable s and other techniques
|
||
# based on arxiv.1210.4539
|
||
# a + i*b
|
||
# p + i*q = ---------
|
||
# c + i*d
|
||
function /(z::Complex128, w::Complex128)
|
||
a, b = reim(z); c, d = reim(w)
|
||
half = 0.5
|
||
two = 2.0
|
||
ab = max(abs(a), abs(b))
|
||
cd = max(abs(c), abs(d))
|
||
ov = realmax(a)
|
||
un = realmin(a)
|
||
ϵ = eps(Float64)
|
||
bs = two/(ϵ*ϵ)
|
||
s = 1.0
|
||
ab >= half*ov && (a=half*a; b=half*b; s=two*s ) # scale down a,b
|
||
cd >= half*ov && (c=half*c; d=half*d; s=s*half) # scale down c,d
|
||
ab <= un*two/ϵ && (a=a*bs; b=b*bs; s=s/bs ) # scale up a,b
|
||
cd <= un*two/ϵ && (c=c*bs; d=d*bs; s=s*bs ) # scale up c,d
|
||
abs(d)<=abs(c) ? ((p,q)=robust_cdiv1(a,b,c,d) ) : ((p,q)=robust_cdiv1(b,a,d,c); q=-q)
|
||
return Complex128(p*s,q*s) # undo scaling
|
||
end
|
||
function robust_cdiv1(a::Float64, b::Float64, c::Float64, d::Float64)
|
||
r = d/c
|
||
t = 1.0/(c+d*r)
|
||
p = robust_cdiv2(a,b,c,d,r,t)
|
||
q = robust_cdiv2(b,-a,c,d,r,t)
|
||
return p,q
|
||
end
|
||
function robust_cdiv2(a::Float64, b::Float64, c::Float64, d::Float64, r::Float64, t::Float64)
|
||
if r != 0
|
||
br = b*r
|
||
return (br != 0 ? (a+br)*t : a*t + (b*t)*r)
|
||
else
|
||
return (a + d*(b/c)) * t
|
||
end
|
||
end
|
||
|
||
function inv(w::Complex128)
|
||
c, d = reim(w)
|
||
half = 0.5
|
||
two = 2.0
|
||
cd = max(abs(c), abs(d))
|
||
ov = realmax(c)
|
||
un = realmin(c)
|
||
ϵ = eps(Float64)
|
||
bs = two/(ϵ*ϵ)
|
||
s = 1.0
|
||
cd >= half*ov && (c=half*c; d=half*d; s=s*half) # scale down c,d
|
||
cd <= un*two/ϵ && (c=c*bs; d=d*bs; s=s*bs ) # scale up c,d
|
||
if abs(d)<=abs(c)
|
||
r = d/c
|
||
t = 1.0/(c+d*r)
|
||
p = t
|
||
q = -r * t
|
||
else
|
||
c, d = d, c
|
||
r = d/c
|
||
t = 1.0/(c+d*r)
|
||
p = r * t
|
||
q = -t
|
||
end
|
||
return Complex128(p*s,q*s) # undo scaling
|
||
end
|
||
|
||
function ssqs(x::T, y::T) where T<:AbstractFloat
|
||
k::Int = 0
|
||
ρ = x*x + y*y
|
||
if !isfinite(ρ) && (isinf(x) || isinf(y))
|
||
ρ = convert(T, Inf)
|
||
elseif isinf(ρ) || (ρ==0 && (x!=0 || y!=0)) || ρ<nextfloat(zero(T))/(2*eps(T)^2)
|
||
m::T = max(abs(x), abs(y))
|
||
k = m==0 ? m : exponent(m)
|
||
xk, yk = ldexp(x,-k), ldexp(y,-k)
|
||
ρ = xk*xk + yk*yk
|
||
end
|
||
ρ, k
|
||
end
|
||
|
||
function sqrt(z::Complex{<:AbstractFloat})
|
||
x, y = reim(z)
|
||
if x==y==0
|
||
return Complex(zero(x),y)
|
||
end
|
||
ρ, k::Int = ssqs(x, y)
|
||
if isfinite(x) ρ=ldexp(abs(x),-k)+sqrt(ρ) end
|
||
if isodd(k)
|
||
k = div(k-1,2)
|
||
else
|
||
k = div(k,2)-1
|
||
ρ += ρ
|
||
end
|
||
ρ = ldexp(sqrt(ρ),k) #sqrt((abs(z)+abs(x))/2) without over/underflow
|
||
ξ = ρ
|
||
η = y
|
||
if ρ != 0
|
||
if isfinite(η) η=(η/ρ)/2 end
|
||
if x<0
|
||
ξ = abs(η)
|
||
η = copysign(ρ,y)
|
||
end
|
||
end
|
||
Complex(ξ,η)
|
||
end
|
||
sqrt(z::Complex) = sqrt(float(z))
|
||
|
||
# function sqrt(z::Complex)
|
||
# rz = float(real(z))
|
||
# iz = float(imag(z))
|
||
# r = sqrt((hypot(rz,iz)+abs(rz))/2)
|
||
# if r == 0
|
||
# return Complex(zero(iz), iz)
|
||
# end
|
||
# if rz >= 0
|
||
# return Complex(r, iz/r/2)
|
||
# end
|
||
# return Complex(abs(iz)/r/2, copysign(r,iz))
|
||
# end
|
||
|
||
# compute exp(im*theta)
|
||
cis(theta::Real) = Complex(cos(theta),sin(theta))
|
||
|
||
"""
|
||
cis(z)
|
||
|
||
Return ``\\exp(iz)``.
|
||
"""
|
||
function cis(z::Complex)
|
||
v = exp(-imag(z))
|
||
Complex(v*cos(real(z)), v*sin(real(z)))
|
||
end
|
||
|
||
"""
|
||
angle(z)
|
||
|
||
Compute the phase angle in radians of a complex number `z`.
|
||
"""
|
||
angle(z::Complex) = atan2(imag(z), real(z))
|
||
|
||
function log(z::Complex{T}) where T<:AbstractFloat
|
||
const T1::T = 1.25
|
||
const T2::T = 3
|
||
const ln2::T = log(convert(T,2)) #0.6931471805599453
|
||
x, y = reim(z)
|
||
ρ, k = ssqs(x,y)
|
||
ax = abs(x)
|
||
ay = abs(y)
|
||
if ax < ay
|
||
θ, β = ax, ay
|
||
else
|
||
θ, β = ay, ax
|
||
end
|
||
if k==0 && (0.5 < β*β) && (β <= T1 || ρ < T2)
|
||
ρρ = log1p((β-1)*(β+1)+θ*θ)/2
|
||
else
|
||
ρρ = log(ρ)/2 + k*ln2
|
||
end
|
||
Complex(ρρ, angle(z))
|
||
end
|
||
log(z::Complex) = log(float(z))
|
||
|
||
# function log(z::Complex)
|
||
# ar = abs(real(z))
|
||
# ai = abs(imag(z))
|
||
# if ar < ai
|
||
# r = ar/ai
|
||
# re = log(ai) + log1p(r*r)/2
|
||
# else
|
||
# if ar == 0
|
||
# re = isnan(ai) ? ai : -inv(ar)
|
||
# elseif isinf(ai)
|
||
# re = oftype(ar,Inf)
|
||
# else
|
||
# r = ai/ar
|
||
# re = log(ar) + log1p(r*r)/2
|
||
# end
|
||
# end
|
||
# Complex(re, angle(z))
|
||
# end
|
||
|
||
function log10(z::Complex)
|
||
a = log(z)
|
||
a/log(oftype(real(a),10))
|
||
end
|
||
function log2(z::Complex)
|
||
a = log(z)
|
||
a/log(oftype(real(a),2))
|
||
end
|
||
|
||
function exp(z::Complex)
|
||
zr, zi = reim(z)
|
||
if isnan(zr)
|
||
Complex(zr, zi==0 ? zi : zr)
|
||
elseif !isfinite(zi)
|
||
if zr == Inf
|
||
Complex(-zr, oftype(zr,NaN))
|
||
elseif zr == -Inf
|
||
Complex(-zero(zr), copysign(zero(zi), zi))
|
||
else
|
||
Complex(oftype(zr,NaN), oftype(zi,NaN))
|
||
end
|
||
else
|
||
er = exp(zr)
|
||
if iszero(zi)
|
||
Complex(er, zi)
|
||
else
|
||
Complex(er*cos(zi), er*sin(zi))
|
||
end
|
||
end
|
||
end
|
||
|
||
function expm1(z::Complex{T}) where T<:Real
|
||
Tf = float(T)
|
||
zr,zi = reim(z)
|
||
if isnan(zr)
|
||
Complex(zr, zi==0 ? zi : zr)
|
||
elseif !isfinite(zi)
|
||
if zr == Inf
|
||
Complex(-zr, oftype(zr,NaN))
|
||
elseif zr == -Inf
|
||
Complex(-one(zr), copysign(zero(zi), zi))
|
||
else
|
||
Complex(oftype(zr,NaN), oftype(zi,NaN))
|
||
end
|
||
else
|
||
erm1 = expm1(zr)
|
||
if zi == 0
|
||
Complex(erm1, zi)
|
||
else
|
||
er = erm1+one(erm1)
|
||
if isfinite(er)
|
||
wr = erm1 - 2 * er * (sin(convert(Tf, 0.5) * zi))^2
|
||
return Complex(wr, er * sin(zi))
|
||
else
|
||
return Complex(er * cos(zi), er * sin(zi))
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
function log1p(z::Complex{T}) where T
|
||
zr,zi = reim(z)
|
||
if isfinite(zr)
|
||
isinf(zi) && return log(z)
|
||
# This is based on a well-known trick for log1p of real z,
|
||
# allegedly due to Kahan, only modified to handle real(u) <= 0
|
||
# differently to avoid inaccuracy near z==-2 and for correct branch cut
|
||
u = float(one(T)) + z
|
||
u == 1 ? convert(typeof(u), z) : real(u) <= 0 ? log(u) : log(u)*z/(u-1)
|
||
elseif isnan(zr)
|
||
Complex(zr, zr)
|
||
elseif isfinite(zi)
|
||
Complex(T(Inf), copysign(zr > 0 ? zero(T) : convert(T, pi), zi))
|
||
else
|
||
Complex(T(Inf), T(NaN))
|
||
end
|
||
end
|
||
|
||
function ^(z::Complex{T}, p::Complex{T})::Complex{T} where T<:AbstractFloat
|
||
if p == 2 #square
|
||
zr, zi = reim(z)
|
||
x = (zr-zi)*(zr+zi)
|
||
y = 2*zr*zi
|
||
if isnan(x)
|
||
if isinf(y)
|
||
x = copysign(zero(T),zr)
|
||
elseif isinf(zi)
|
||
x = convert(T,-Inf)
|
||
elseif isinf(zr)
|
||
x = convert(T,Inf)
|
||
end
|
||
elseif isnan(y) && isinf(x)
|
||
y = copysign(zero(T), y)
|
||
end
|
||
Complex(x,y)
|
||
elseif z!=0
|
||
if p!=0 && isinteger(p)
|
||
rp = real(p)
|
||
if rp < 0
|
||
return power_by_squaring(inv(z), convert(Integer, -rp))
|
||
else
|
||
return power_by_squaring(z, convert(Integer, rp))
|
||
end
|
||
end
|
||
exp(p*log(z))
|
||
elseif p!=0 #0^p
|
||
zero(z) #CHECK SIGNS
|
||
else #0^0
|
||
zer = copysign(zero(T),real(p))*copysign(zero(T),imag(z))
|
||
Complex(one(T), zer)
|
||
end
|
||
end
|
||
|
||
function exp2(z::Complex{T}) where T<:AbstractFloat
|
||
er = exp2(real(z))
|
||
theta = imag(z) * log(convert(T, 2))
|
||
Complex(er*cos(theta), er*sin(theta))
|
||
end
|
||
exp2(z::Complex) = exp2(float(z))
|
||
|
||
function exp10(z::Complex{T}) where T<:AbstractFloat
|
||
er = exp10(real(z))
|
||
theta = imag(z) * log(convert(T, 10))
|
||
Complex(er*cos(theta), er*sin(theta))
|
||
end
|
||
exp10(z::Complex) = exp10(float(z))
|
||
|
||
function ^(z::T, p::T) where T<:Complex
|
||
if isinteger(p)
|
||
rp = real(p)
|
||
if rp < 0
|
||
return power_by_squaring(inv(float(z)), convert(Integer, -rp))
|
||
else
|
||
return power_by_squaring(float(z), convert(Integer, rp))
|
||
end
|
||
end
|
||
pr, pim = reim(p)
|
||
zr, zi = reim(z)
|
||
r = abs(z)
|
||
rp = r^pr
|
||
theta = atan2(zi, zr)
|
||
ntheta = pr*theta
|
||
if pim != 0 && r != 0
|
||
rp = rp*exp(-pim*theta)
|
||
ntheta = ntheta + pim*log(r)
|
||
end
|
||
cosntheta = cos(ntheta)
|
||
sinntheta = sin(ntheta)
|
||
re, im = rp*cosntheta, rp*sinntheta
|
||
if isinf(rp)
|
||
if isnan(re)
|
||
re = copysign(zero(re), cosntheta)
|
||
end
|
||
if isnan(im)
|
||
im = copysign(zero(im), sinntheta)
|
||
end
|
||
end
|
||
|
||
# apply some corrections to force known zeros
|
||
if pim == 0
|
||
if isinteger(pr)
|
||
if zi == 0
|
||
im = copysign(zero(im), im)
|
||
elseif zr == 0
|
||
if isinteger(0.5*pr) # pr is even
|
||
im = copysign(zero(im), im)
|
||
else
|
||
re = copysign(zero(re), re)
|
||
end
|
||
end
|
||
else
|
||
dr = pr*2
|
||
if isinteger(dr) && zi == 0
|
||
if zr < 0
|
||
re = copysign(zero(re), re)
|
||
else
|
||
im = copysign(zero(im), im)
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
Complex(re, im)
|
||
end
|
||
|
||
^(z::Complex, n::Bool) = n ? z : one(z)
|
||
^(z::Complex, n::Integer) = z^Complex(n)
|
||
|
||
^(z::Complex{<:AbstractFloat}, n::Bool) = n ? z : one(z) # to resolve ambiguity
|
||
^(z::Complex{<:Integer}, n::Bool) = n ? z : one(z) # to resolve ambiguity
|
||
|
||
^(z::Complex{<:AbstractFloat}, n::Integer) =
|
||
n>=0 ? power_by_squaring(z,n) : power_by_squaring(inv(z),-n)
|
||
^(z::Complex{<:Integer}, n::Integer) = power_by_squaring(z,n) # DomainError for n<0
|
||
|
||
function sin(z::Complex{T}) where T
|
||
F = float(T)
|
||
zr, zi = reim(z)
|
||
if zr == 0
|
||
Complex(F(zr), sinh(zi))
|
||
elseif !isfinite(zr)
|
||
if zi == 0 || isinf(zi)
|
||
Complex(F(NaN), F(zi))
|
||
else
|
||
Complex(F(NaN), F(NaN))
|
||
end
|
||
else
|
||
Complex(sin(zr)*cosh(zi), cos(zr)*sinh(zi))
|
||
end
|
||
end
|
||
|
||
|
||
function cos(z::Complex{T}) where T
|
||
F = float(T)
|
||
zr, zi = reim(z)
|
||
if zr == 0
|
||
Complex(cosh(zi), isnan(zi) ? F(zr) : -flipsign(F(zr),zi))
|
||
elseif !isfinite(zr)
|
||
if zi == 0
|
||
Complex(F(NaN), isnan(zr) ? zero(F) : -flipsign(F(zi),zr))
|
||
elseif isinf(zi)
|
||
Complex(F(Inf), F(NaN))
|
||
else
|
||
Complex(F(NaN), F(NaN))
|
||
end
|
||
else
|
||
Complex(cos(zr)*cosh(zi), -sin(zr)*sinh(zi))
|
||
end
|
||
end
|
||
|
||
|
||
function tan(z::Complex)
|
||
zr, zi = reim(z)
|
||
w = tanh(Complex(-zi, zr))
|
||
Complex(imag(w), -real(w))
|
||
end
|
||
|
||
function asin(z::Complex)
|
||
zr, zi = reim(z)
|
||
if isinf(zr) && isinf(zi)
|
||
return Complex(copysign(oftype(zr,pi)/4, zr),zi)
|
||
elseif isnan(zi) && isinf(zr)
|
||
return Complex(zi, oftype(zr, Inf))
|
||
end
|
||
ξ = zr == 0 ? zr :
|
||
!isfinite(zr) ? oftype(zr,pi)/2 * sign(zr) :
|
||
atan2(zr, real(sqrt(1-z)*sqrt(1+z)))
|
||
η = asinh(copysign(imag(sqrt(conj(1-z))*sqrt(1+z)), imag(z)))
|
||
Complex(ξ,η)
|
||
end
|
||
|
||
function acos(z::Complex{<:AbstractFloat})
|
||
zr, zi = reim(z)
|
||
if isnan(zr)
|
||
if isinf(zi) return Complex(zr, -zi)
|
||
else return Complex(zr, zr) end
|
||
elseif isnan(zi)
|
||
if isinf(zr) return Complex(zi, abs(zr))
|
||
elseif zr==0 return Complex(oftype(zr,pi)/2, zi)
|
||
else return Complex(zi, zi) end
|
||
elseif zr==zi==0
|
||
return Complex(oftype(zr,pi)/2, -zi)
|
||
elseif zr==Inf && zi===0.0
|
||
return Complex(zi, -zr)
|
||
elseif zr==-Inf && zi===-0.0
|
||
return Complex(oftype(zi,pi), -zr)
|
||
end
|
||
ξ = 2*atan2(real(sqrt(1-z)), real(sqrt(1+z)))
|
||
η = asinh(imag(sqrt(conj(1+z))*sqrt(1-z)))
|
||
if isinf(zr) && isinf(zi) ξ -= oftype(η,pi)/4 * sign(zr) end
|
||
Complex(ξ,η)
|
||
end
|
||
acos(z::Complex) = acos(float(z))
|
||
|
||
function atan(z::Complex)
|
||
w = atanh(Complex(-imag(z),real(z)))
|
||
Complex(imag(w),-real(w))
|
||
end
|
||
|
||
function sinh(z::Complex)
|
||
zr, zi = reim(z)
|
||
w = sin(Complex(zi, zr))
|
||
Complex(imag(w),real(w))
|
||
end
|
||
|
||
function cosh(z::Complex)
|
||
zr, zi = reim(z)
|
||
cos(Complex(zi,-zr))
|
||
end
|
||
|
||
function tanh(z::Complex{T}) where T<:AbstractFloat
|
||
const Ω = prevfloat(typemax(T))
|
||
ξ, η = reim(z)
|
||
if isnan(ξ) && η==0 return Complex(ξ, η) end
|
||
if 4*abs(ξ) > asinh(Ω) #Overflow?
|
||
Complex(copysign(one(T),ξ),
|
||
copysign(zero(T),η*(isfinite(η) ? sin(2*abs(η)) : one(η))))
|
||
else
|
||
t = tan(η)
|
||
β = 1+t*t #sec(η)^2
|
||
s = sinh(ξ)
|
||
ρ = sqrt(1 + s*s) #cosh(ξ)
|
||
if isinf(t)
|
||
Complex(ρ/s,1/t)
|
||
else
|
||
Complex(β*ρ*s,t)/(1+β*s*s)
|
||
end
|
||
end
|
||
end
|
||
tanh(z::Complex) = tanh(float(z))
|
||
|
||
function asinh(z::Complex)
|
||
w = asin(Complex(-imag(z),real(z)))
|
||
Complex(imag(w),-real(w))
|
||
end
|
||
|
||
function acosh(z::Complex)
|
||
zr, zi = reim(z)
|
||
if isnan(zr) || isnan(zi)
|
||
if isinf(zr) || isinf(zi)
|
||
return Complex(oftype(zr, Inf), oftype(zi, NaN))
|
||
else
|
||
return Complex(oftype(zr, NaN), oftype(zi, NaN))
|
||
end
|
||
elseif zr==-Inf && zi===-0.0 #Edge case is wrong - WHY?
|
||
return Complex(oftype(zr,Inf), oftype(zi, -pi))
|
||
end
|
||
ξ = asinh(real(sqrt(conj(z-1))*sqrt(z+1)))
|
||
η = 2atan2(imag(sqrt(z-1)),real(sqrt(z+1)))
|
||
if isinf(zr) && isinf(zi)
|
||
η -= oftype(η,pi)/4 * sign(zi) * sign(zr)
|
||
end
|
||
Complex(ξ, η)
|
||
end
|
||
|
||
function atanh(z::Complex{T}) where T<:AbstractFloat
|
||
const Ω = prevfloat(typemax(T))
|
||
const θ = sqrt(Ω)/4
|
||
const ρ = 1/θ
|
||
x, y = reim(z)
|
||
ax = abs(x)
|
||
ay = abs(y)
|
||
if ax > θ || ay > θ #Prevent overflow
|
||
if isnan(y)
|
||
if isinf(x)
|
||
return Complex(copysign(zero(x),x), y)
|
||
else
|
||
return Complex(real(1/z), y)
|
||
end
|
||
end
|
||
if isinf(y)
|
||
return Complex(copysign(zero(x),x), copysign(oftype(y,pi)/2, y))
|
||
end
|
||
return Complex(real(1/z), copysign(oftype(y,pi)/2, y))
|
||
elseif ax==1
|
||
if y == 0
|
||
ξ = copysign(oftype(x,Inf),x)
|
||
η = zero(y)
|
||
else
|
||
ym = ay+ρ
|
||
ξ = log(sqrt(sqrt(4+y*y))/sqrt(ym))
|
||
η = copysign(oftype(y,pi)/2 + atan(ym/2), y)/2
|
||
end
|
||
else #Normal case
|
||
ysq = (ay+ρ)^2
|
||
if x == 0
|
||
ξ = x
|
||
else
|
||
ξ = log1p(4x/((1-x)^2 + ysq))/4
|
||
end
|
||
η = angle(Complex((1-x)*(1+x)-ysq, 2y))/2
|
||
end
|
||
Complex(ξ, η)
|
||
end
|
||
atanh(z::Complex) = atanh(float(z))
|
||
|
||
function lexcmp(a::Complex, b::Complex)
|
||
c = cmp(real(a), real(b))
|
||
c == 0 || return c
|
||
cmp(imag(a), imag(b))
|
||
end
|
||
|
||
#Rounding complex numbers
|
||
#Requires two different RoundingModes for the real and imaginary components
|
||
"""
|
||
round(z, RoundingModeReal, RoundingModeImaginary)
|
||
|
||
Returns the nearest integral value of the same type as the complex-valued `z` to `z`,
|
||
breaking ties using the specified [`RoundingMode`](@ref)s. The first
|
||
[`RoundingMode`](@ref) is used for rounding the real components while the
|
||
second is used for rounding the imaginary components.
|
||
"""
|
||
function round(z::Complex{<:AbstractFloat}, ::RoundingMode{MR}, ::RoundingMode{MI}) where {MR,MI}
|
||
Complex(round(real(z), RoundingMode{MR}()),
|
||
round(imag(z), RoundingMode{MI}()))
|
||
end
|
||
round(z::Complex) = Complex(round(real(z)), round(imag(z)))
|
||
|
||
function round(z::Complex, digits::Integer, base::Integer=10)
|
||
Complex(round(real(z), digits, base),
|
||
round(imag(z), digits, base))
|
||
end
|
||
|
||
float(z::Complex{<:AbstractFloat}) = z
|
||
float(z::Complex) = Complex(float(real(z)), float(imag(z)))
|
||
|
||
big(z::Complex{<:AbstractFloat}) = Complex{BigFloat}(z)
|
||
big(z::Complex{<:Integer}) = Complex{BigInt}(z)
|
||
|
||
## Array operations on complex numbers ##
|
||
|
||
complex(A::AbstractArray{<:Complex}) = A
|
||
|
||
function complex(A::AbstractArray{T}) where T
|
||
if !isleaftype(T)
|
||
error("`complex` not defined on abstractly-typed arrays; please convert to a more specific type")
|
||
end
|
||
convert(AbstractArray{typeof(complex(zero(T)))}, A)
|
||
end
|
||
|
||
## promotion to complex ##
|
||
|
||
_default_type(T::Type{Complex}) = Complex{Int}
|