Skip to content

Commit 94cee82

Browse files
author
Marek P
committed
2 parents a100ffc + 7fa544e commit 94cee82

File tree

3 files changed

+121
-65
lines changed

3 files changed

+121
-65
lines changed

src/Polynomials.jl

Lines changed: 87 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,14 @@ export Pade, padeval
1414

1515
import Base: length, endof, getindex, setindex!, copy, zero, one, convert, norm, gcd
1616
import Base: show, print, *, /, //, -, +, ==, divrem, div, rem, eltype, .*, .-, .+
17-
import Base: promote_rule, truncate, chop
18-
if VERSION >= v"0.4"
19-
import Base.call
20-
end
17+
import Base: promote_rule, truncate, chop, call, conj, transpose, dot, hash
18+
import Base: isequal
2119

2220
eps{T}(::Type{T}) = zero(T)
2321
eps{F<:AbstractFloat}(x::Type{F}) = Base.eps(F)
2422
eps{T}(x::Type{Complex{T}}) = eps(T)
2523

24+
typealias SymbolLike Union{AbstractString,Char,Symbol}
2625

2726
"""
2827
@@ -60,22 +59,22 @@ p + q # ERROR: Polynomials must have same variable.
6059
```
6160
6261
"""
63-
immutable Poly{T<:Number}
64-
a::Vector{T}
65-
var::Symbol
66-
function Poly(a::Vector{T}, var)
67-
# if a == [] we replace it with a = [0]
68-
if length(a) == 0
69-
return new(zeros(T,1), @compat Symbol(var))
70-
else
71-
# determine the last nonzero element and truncate a accordingly
72-
a_last = max(1,findlast(x->x!=zero(T), a))
73-
new(a[1:a_last], @compat Symbol(var))
74-
end
62+
immutable Poly{T}
63+
a::Vector{T}
64+
var::Symbol
65+
@compat function (::Type{Poly}){T<:Number}(a::Vector{T}, var::SymbolLike = :x)
66+
# if a == [] we replace it with a = [0]
67+
if length(a) == 0
68+
return new{T}(zeros(T,1), @compat Symbol(var))
69+
else
70+
# determine the last nonzero element and truncate a accordingly
71+
a_last = max(1,findlast(x->x!=zero(T), a))
72+
new{T}(a[1:a_last], @compat Symbol(var))
7573
end
74+
end
7675
end
7776

78-
@compat Poly{T<:Number}(a::Vector{T}, var::Union{AbstractString,Symbol,Char}=:x) = Poly{T}(a, var)
77+
Poly(n::Number, var::SymbolLike = :x) = Poly([n], var)
7978

8079
# create a Poly object from its roots
8180
"""
@@ -90,7 +89,7 @@ Example:
9089
poly([1,2,3]) # Poly(-6 + 11x - 6x^2 + x^3)
9190
```
9291
"""
93-
function poly{T}(r::AbstractVector{T}, var=:x)
92+
function poly{T}(r::AbstractVector{T}, var::SymbolLike=:x)
9493
n = length(r)
9594
c = zeros(T, n+1)
9695
c[1] = one(T)
@@ -101,9 +100,7 @@ function poly{T}(r::AbstractVector{T}, var=:x)
101100
end
102101
return Poly(reverse(c), var)
103102
end
104-
poly(A::Matrix, var=:x) = poly(eigvals(A), var)
105-
poly(A::Matrix, var::AbstractString) = poly(eigvals(A), @compat Symbol(var))
106-
poly(A::Matrix, var::Char) = poly(eig(A)[1], @compat Symbol(var))
103+
poly(A::Matrix, var::SymbolLike=:x) = poly(eigvals(A), var)
107104

108105

109106
include("show.jl") # display polynomials.
@@ -148,9 +145,9 @@ Return the indeterminate of a polynomial, `x`.
148145
* `variable([var::Symbol])`: return polynomial 1x over `Float64`.
149146
150147
"""
151-
variable{T<:Number}(::Type{T}, var=:x) = Poly([zero(T), one(T)], var)
148+
variable{T<:Number}(::Type{T}, var::SymbolLike=:x) = Poly([zero(T), one(T)], var)
152149
variable{T}(p::Poly{T}) = variable(T, p.var)
153-
variable(var::Symbol=:x) = variable(Float64, var)
150+
variable(var::SymbolLike=:x) = variable(Float64, var)
154151

155152
"""
156153
@@ -206,7 +203,10 @@ norm(q::Poly, args...) = norm(coeffs(q), args...)
206203
* `conj(p::Poly`): return conjugate of polynomial `p`. (Polynomial with conjugate of each coefficient.)
207204
208205
"""
209-
Base.conj{T<:Complex}(p::Poly{T}) = Poly(conj(coeffs(p)))
206+
conj{T<:Complex}(p::Poly{T}) = Poly(conj(coeffs(p)))
207+
208+
# Define the no-op `transpose` explicitly to avoid future warnings in Julia
209+
transpose(p::Poly) = p
210210

211211
"""
212212
@@ -228,21 +228,22 @@ function setindex!(p::Poly, vs, idx::AbstractArray)
228228
[setindex!(p, v, i) for (i,v) in zip(idx, vs)]
229229
p
230230
end
231-
Base.eachindex{T}(p::Poly{T}) = 0:(length(p)-1)
231+
eachindex{T}(p::Poly{T}) = 0:(length(p)-1)
232+
232233

233-
234234
copy(p::Poly) = Poly(copy(p.a), p.var)
235235

236-
zero{T}(p::Poly{T}) = Poly([zero(T)], p.var)
236+
zero{T}(p::Poly{T}) = Poly(T[], p.var)
237237
zero{T}(::Type{Poly{T}}) = Poly(T[])
238238
one{T}(p::Poly{T}) = Poly([one(T)], p.var)
239239
one{T}(::Type{Poly{T}}) = Poly([one(T)])
240240

241241
## Overload arithmetic operators for polynomial operations between polynomials and scalars
242242
*{T<:Number,S}(c::T, p::Poly{S}) = Poly(c * p.a, p.var)
243243
*{T<:Number,S}(p::Poly{S}, c::T) = Poly(p.a * c, p.var)
244-
Base.dot{T<:Number,S}(p::Poly{S}, c::T) = p * c
245-
Base.dot{T<:Number,S}(c::T, p::Poly{S}) = c * p
244+
dot{T<:Number,S}(p::Poly{S}, c::T) = p * c
245+
dot{T<:Number,S}(c::T, p::Poly{S}) = c * p
246+
dot(p1::Poly, p2::Poly) = p1 * p2
246247
.*{T<:Number,S}(c::T, p::Poly{S}) = Poly(c * p.a, p.var)
247248
.*{T<:Number,S}(p::Poly{S}, c::T) = Poly(p.a * c, p.var)
248249
/(p::Poly, c::Number) = Poly(p.a / c, p.var)
@@ -336,7 +337,6 @@ function divrem{T, S}(num::Poly{T}, den::Poly{S})
336337
return pQ, pR
337338
end
338339

339-
@deprecate /(num::Poly, den::Poly) div(num::Poly, den::Poly)
340340
div(num::Poly, den::Poly) = divrem(num, den)[1]
341341
rem(num::Poly, den::Poly) = divrem(num, den)[2]
342342

@@ -348,7 +348,8 @@ function ==(p1::Poly, p2::Poly)
348348
end
349349
end
350350

351-
Base.hash(f::Poly, h::UInt) = hash(f.var, hash(f.a, h))
351+
hash(f::Poly, h::UInt) = hash(f.var, hash(f.a, h))
352+
isequal(p1::Poly, p2::Poly) = hash(p1) == hash(p2)
352353

353354
"""
354355
* `polyval(p::Poly, x::Number)`: Evaluate the polynomial `p` at `x` using Horner's method.
@@ -372,19 +373,17 @@ function polyval{T,S}(p::Poly{T}, x::S)
372373
if lenp == 0
373374
return zero(R) * x
374375
else
375-
y = convert(R, p[end]) + zero(T)*x
376+
y = convert(R, p[end])
376377
for i = (endof(p)-1):-1:0
377378
y = p[i] + x*y
378379
end
379380
return y
380381
end
381382
end
382383

383-
polyval(p::Poly, v::AbstractVector) = map(x->polyval(p, x), v)
384+
polyval(p::Poly, v::AbstractArray) = map(x->polyval(p, x), v)
384385

385-
if VERSION >= v"0.4"
386-
@compat (p::Poly)(x) = polyval(p, x)
387-
end
386+
@compat (p::Poly)(x) = polyval(p, x)
388387

389388
"""
390389
@@ -399,9 +398,33 @@ polyint(Poly([1, 0, -1]), 2) # Poly(2.0 + x - 0.3333333333333333x^3)
399398
```
400399
401400
"""
402-
function polyint{T}(p::Poly{T}, k::Number=0)
401+
# if we do not have any initial condition, assume k = zero(Int)
402+
polyint{T}(p::Poly{T}) = polyint(p, 0)
403+
404+
# if we have coefficients that have `NaN` representation
405+
function polyint{T<:Union{Real,Complex},S<:Number}(p::Poly{T}, k::S)
406+
any(isnan(p.a)) && return Poly(promote_type(T,S)[NaN])
407+
_polyint(p, k)
408+
end
409+
410+
# if we have initial condition that can represent `NaN`
411+
function polyint{T,S<:Union{Real,Complex}}(p::Poly{T}, k::S)
412+
isnan(k) && return Poly(promote_type(T,S)[NaN])
413+
_polyint(p, k)
414+
end
415+
416+
# if we have both coefficients and initial condition that can take `NaN`
417+
function polyint{T<:Union{Real,Complex},S<:Union{Real,Complex}}(p::Poly{T}, k::S)
418+
(any(isnan(p.a)) || isnan(k)) && return Poly(promote_type(T,S)[NaN])
419+
_polyint(p, k)
420+
end
421+
422+
# otherwise, catch all
423+
polyint{T,S<:Number}(p::Poly{T}, k::S) = _polyint(p, k)
424+
425+
function _polyint{T,S<:Number}(p::Poly{T}, k::S)
403426
n = length(p)
404-
R = typeof(one(T)/1)
427+
R = promote_type(typeof(one(T)/1), S)
405428
a2 = Array(R, n+1)
406429
a2[1] = k
407430
for i = 1:n
@@ -421,30 +444,37 @@ Example:
421444
polyder(Poly([1, 3, -1])) # Poly(3 - 2x)
422445
```
423446
"""
447+
# if we have coefficients that can represent `NaN`s
448+
function polyder{T<:Union{Real,Complex}}(p::Poly{T}, order::Int=1)
449+
n = length(p)
450+
order < 0 && error("Order of derivative must be non-negative")
451+
order == 0 && return p
452+
any(isnan(p.a)) && return Poly(T[NaN], p.var)
453+
n <= order && return Poly(T[], p.var)
454+
_polyder(p, order)
455+
end
456+
457+
# otherwise
424458
function polyder{T}(p::Poly{T}, order::Int=1)
425-
n = length(p)
426-
if order < 0
427-
error("Order of derivative must be non-negative")
428-
elseif n <= order
429-
return Poly(zeros(T,0),p.var)
430-
elseif order == 0
431-
return p
432-
else
433-
a2 = Array(T, n-order)
434-
for i = order:n-1
435-
a2[i-order+1] = p[i] * prod((i-order+1):i)
436-
end
437-
return Poly(a2, p.var)
438-
end
459+
n = length(p)
460+
order < 0 && error("Order of derivative must be non-negative")
461+
order == 0 && return p
462+
n <= order && return Poly(T[], p.var)
463+
_polyder(p, order)
439464
end
440-
Base.ctranspose{T}(p::Poly{T}) = polyder(p)
441465

442-
polyint{T}(a::Array{Poly{T},1}, k::Number = 0) = [ polyint(p,k) for p in a ]
443-
polyder{T}(a::Array{Poly{T},1}, order::Int = 1) = [ polyder(p,order) for p in a ]
444-
polyint{n,T}(a::Array{Poly{T},n}, k::Number = 0) = map(p->polyint(p,k),a)
445-
polyder{n,T}(a::Array{Poly{T},n}, order::Int = 1) = map(p->polyder(p,order),a)
466+
function _polyder{T}(p::Poly{T}, order::Int=1)
467+
n = length(p)
468+
a2 = Array(T, n-order)
469+
for i = order:n-1
470+
a2[i-order+1] = p[i] * prod((i-order+1):i)
471+
end
446472

473+
return Poly(a2, p.var)
474+
end
447475

476+
polyint{T}(a::AbstractArray{Poly{T}}, k::Number = 0) = map(p->polyint(p,k), a)
477+
polyder{T}(a::AbstractArray{Poly{T}}, order::Int = 1) = map(p->polyder(p,order),a)
448478

449479
##################################################
450480
##

src/show.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ function printproductsign{T}(io::IO, pj::T, j, mimetype)
113113
end
114114

115115
function printcoefficient{T}(io::IO, pj::Complex{T}, j, mimetype)
116-
116+
117117
hasreal = abs(real(pj)) > 0
118118
hasimag = abs(imag(pj)) > 0
119119

@@ -123,7 +123,7 @@ function printcoefficient{T}(io::IO, pj::Complex{T}, j, mimetype)
123123
print(io, ')')
124124
elseif hasreal
125125
a = real(pj)
126-
(showone(T) || a != one(T)) && show(io, mimetype, a)
126+
(j==0 || showone(T) || a != one(T)) && show(io, mimetype, a)
127127
elseif hasimag
128128
b = imag(pj)
129129
(showone(T) || b != one(T)) && show(io, mimetype, b)

test/runtests.jl

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,14 @@ sprint(show, pNULL)
4747
@test polyval(poly([1//2, 3//2]), 1//2) == 0//1
4848
@test polyder(polyint(pN)) == pN
4949
@test polyder(pR) == Poly([-2//1,2//1])
50+
@test_throws ErrorException polyder(pR, -1)
5051
@test polyint(pNULL,1) == p1
5152
@test polyint(Poly(Rational[1,2,3])) == Poly(Rational[0, 1, 1, 1])
5253
@test polyder(p3) == Poly([2,2])
5354
@test polyder(p1) == polyder(p0) == polyder(pNULL) == pNULL
5455

55-
if VERSION >= v"0.4"
56-
@test pN(-.125) == 276.9609375
57-
@test pN([0.1, 0.2, 0.3]) == polyval(pN, [0.1, 0.2, 0.3])
58-
end
56+
@test pN(-.125) == 276.9609375
57+
@test pN([0.1, 0.2, 0.3]) == polyval(pN, [0.1, 0.2, 0.3])
5958

6059
@test poly([-1,-1]) == p3
6160
@test roots(p0)==roots(p1)==roots(pNULL)==[]
@@ -173,7 +172,7 @@ q = [3, p1]
173172
psum = p+3
174173
pprod = p*3
175174
pmin = p-3
176-
@test isa(psum, Vector{Poly{Float64}})
175+
@test isa(psum, Vector{Poly{Float64}})
177176
@test isa(pprod,Vector{Poly{Float64}})
178177
@test isa(pmin, Vector{Poly{Float64}})
179178

@@ -228,6 +227,8 @@ p = Poly([1,2,3,1]) # leading coefficient of 1
228227
@test repr(p) == "Poly(1 + 2⋅x + 3⋅x^2 + x^3)"
229228
p = Poly([1.0, 2.0, 3.0, 1.0])
230229
@test repr(p) == "Poly(1.0 + 2.0⋅x + 3.0⋅x^2 + 1.0⋅x^3)"
230+
p = Poly([1, im])
231+
@test repr(p) == "Poly(1 + im⋅x)"
231232
p = Poly([1+im, 1-im, -1+im, -1 - im])# minus signs
232233
@test repr(p) == "Poly((1 + 1im) + (1 - 1im)⋅x - (1 - 1im)⋅x^2 - (1 + 1im)⋅x^3)"
233234

@@ -245,8 +246,33 @@ r = Poly([1.0, 2, 3])
245246
@test string_eval_poly(p, 5) == p(5)
246247
@test string_eval_poly(q, 5) == q(5)
247248
@test string_eval_poly(r, 5) == r(5)
248-
249+
249250
## check hashing
250251
p = poly([1,2,3])
251252
q = poly([1,2,3])
252253
@test hash(p) == hash(q)
254+
255+
## Check for Inf/NaN operations
256+
p1 = Poly([Inf, Inf])
257+
p2 = Poly([0, Inf])
258+
@test p1(Inf) == Inf
259+
@test isnan(p1(-Inf))
260+
@test isnan(p1(0))
261+
@test p2(-Inf) == -Inf
262+
263+
## Check for isequal
264+
p1 = Poly([-0., 5., Inf])
265+
p2 = Poly([0., 5., Inf])
266+
p3 = Poly([0, NaN])
267+
268+
@test p1 == p2 && !isequal(p1, p2)
269+
@test is(p3, p3) && p3 p3 && isequal(p3, p3)
270+
271+
## Handling of `NaN`s
272+
p = Poly([NaN, 1, 5])
273+
pder = polyder(p)
274+
pint = polyint(p)
275+
276+
@test isnan(p(1)) # p(1) evaluates to NaN
277+
@test isequal(pder, Poly([NaN])) # derivative will give Poly([NaN])
278+
@test isequal(pint, Poly([NaN])) # integral will give Poly([NaN])

0 commit comments

Comments
 (0)