Skip to content

Commit 463c0ae

Browse files
authored
Merge pull request #98 from aytekinar/96-comparison
Implement `isapprox` and `==`
2 parents 91c08b2 + c4fc69e commit 463c0ae

File tree

2 files changed

+69
-35
lines changed

2 files changed

+69
-35
lines changed

src/Polynomials.jl

Lines changed: 47 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,10 @@ export polyval, polyint, polyder, roots, polyfit
1313
export Pade, padeval
1414

1515
import Base: length, endof, getindex, setindex!, copy, zero, one, convert, norm, gcd
16-
import Base: show, print, *, /, //, -, +, ==, divrem, div, rem, eltype
16+
import Base: show, print, *, /, //, -, +, ==, isapprox, divrem, div, rem, eltype
1717
import Base: promote_rule, truncate, chop, call, conj, transpose, dot, hash
1818
import Base: isequal
1919

20-
eps{T}(::Type{T}) = zero(T)
21-
eps{F<:AbstractFloat}(x::Type{F}) = Base.eps(F)
22-
eps{T}(x::Type{Complex{T}}) = eps(T)
23-
2420
typealias SymbolLike Union{AbstractString,Char,Symbol}
2521

2622
"""
@@ -152,34 +148,29 @@ variable{T}(p::Poly{T}) = variable(T, p.var)
152148
variable(var::SymbolLike=:x) = variable(Float64, var)
153149

154150
"""
151+
truncate{T}(p::Poly{T}; reltol::Real = Base.rtoldefault(real(T)), abstol::Real = 0)
155152
156-
`truncate{T}(p::Poly{T}; reltol = eps(T), abstol = eps(T))`: returns a polynomial with coefficients a_i truncated to zero if |a_i| <= reltol*maxabs(a)+abstol
157-
153+
Return a polynomial with coefficients `a_i` truncated to zero if `|a_i| <= reltol*maxabs(a)+abstol`.
158154
"""
159-
function truncate{T}(p::Poly{Complex{T}}; reltol = eps(T), abstol = eps(T))
155+
function truncate{T}(p::Poly{T}; reltol::Real = Base.rtoldefault(real(T)),
156+
abstol::Real = 0)
160157
a = coeffs(p)
161158
amax = maximum(abs,a)
162159
thresh = amax * reltol + abstol
163-
anew = map(ai -> complex(abs(real(ai)) <= thresh ? zero(T) : real(ai),
164-
abs(imag(ai)) <= thresh ? zero(T) : imag(ai)),
165-
a)
166-
return Poly(anew, p.var)
167-
end
168-
169-
function truncate{T}(p::Poly{T}; reltol = eps(T), abstol = eps(T))
170-
a = coeffs(p)
171-
amax = maximum(abs,a)
172-
anew = map(ai -> abs(ai) <= amax*reltol+abstol ? zero(T) : ai, a)
160+
anew = map(ai -> abs(ai) <= thresh ? zero(T) : ai, a)
173161
return Poly(anew, p.var)
174162
end
175163

176164
"""
177165
178-
`chop(p::Poly{T}; kwargs...)` chop off leading values which are
179-
approximately zero. The tolerances are passed to `isapprox`.
166+
chop(p::Poly{T}; reltol::Real = Base.rtoldefault(real(T)), abstol::Real = 0)
167+
168+
Chop off leading values which are approximately zero.
180169
170+
The tolerances `reltol` and `abstol` are passed to `isapprox`.
181171
"""
182-
function chop{T}(p::Poly{T}; reltol=zero(T), abstol=2 * eps(T))
172+
function chop{T}(p::Poly{T}; reltol::Real = Base.rtoldefault(real(T)),
173+
abstol::Real = 0)
183174
c = copy(p.a)
184175
for k=length(c):-1:1
185176
if !isapprox(c[k], zero(T); rtol=reltol, atol=abstol)
@@ -345,14 +336,35 @@ end
345336
div(num::Poly, den::Poly) = divrem(num, den)[1]
346337
rem(num::Poly, den::Poly) = divrem(num, den)[2]
347338

348-
function ==(p1::Poly, p2::Poly)
349-
if p1.var != p2.var
350-
return false
351-
else
352-
return p1.a == p2.a
353-
end
339+
==(p1::Poly, p2::Poly) = (p1.var == p2.var && p1.a == p2.a)
340+
==(p1::Poly, n::Number) = (coeffs(p1) == [n])
341+
==(n::Number, p1::Poly) = (p1 == n)
342+
343+
"""
344+
isapprox{T,S}(p1::Poly{T}, p2::Poly{S}; reltol::Real = Base.rtoldefault(T,S), abstol::Real = 0, norm::Function = vecnorm)
345+
346+
Truncate `p1` and `p2`, and compare the coefficients with `isapprox`.
347+
348+
The tolerances `reltol` and `abstol` are passed to both `truncate` and `isapprox`.
349+
"""
350+
function isapprox{T,S}(p1::Poly{T}, p2::Poly{S};
351+
reltol::Real = Base.rtoldefault(T,S), abstol::Real = 0, norm::Function = vecnorm)
352+
p1.var == p2.var || error("Polynomials must have same variable")
353+
p1t = truncate(p1; reltol = reltol, abstol = abstol)
354+
p2t = truncate(p2; reltol = reltol, abstol = abstol)
355+
length(p1t) == length(p2t) && isapprox(coeffs(p1t), coeffs(p2t); rtol = reltol,
356+
atol = abstol, norm = norm)
354357
end
355358

359+
function isapprox{T,S<:Number}(p1::Poly{T}, n::S; reltol::Real = Base.rtoldefault(T,S),
360+
abstol::Real = 0)
361+
p1t = truncate(p1; reltol = reltol, abstol = abstol)
362+
degree(p1t) == 0 && isapprox(coeffs(p1), [n]; rtol = reltol, atol = abstol)
363+
end
364+
365+
isapprox{T,S<:Number}(n::S, p1::Poly{T}; reltol::Real = Base.rtoldefault(T,S),
366+
abstol::Real = 0) = isapprox(p1, n; reltol = reltol, abstol = abstol)
367+
356368
hash(f::Poly, h::UInt) = hash(f.var, hash(f.a, h))
357369
isequal(p1::Poly, p2::Poly) = hash(p1) == hash(p2)
358370

@@ -505,15 +517,16 @@ roots(poly([1,2,3,4])) # [1.0,2.0,3.0,4.0]
505517
function roots{T}(p::Poly{T})
506518
R = promote_type(T, Float64)
507519
length(p) == 0 && return zeros(R, 0)
520+
p = truncate(p)
508521
num_leading_zeros = 0
509-
while abs(p[num_leading_zeros]) <= 2*eps(T)
522+
while p[num_leading_zeros] zero(T)
510523
if num_leading_zeros == length(p)-1
511524
return zeros(R, 0)
512525
end
513526
num_leading_zeros += 1
514527
end
515528
num_trailing_zeros = 0
516-
while abs(p[end - num_trailing_zeros]) <= 2*eps(T)
529+
while p[end - num_trailing_zeros] zero(T)
517530
num_trailing_zeros += 1
518531
end
519532
n = endof(p)-(num_leading_zeros + num_trailing_zeros)
@@ -543,12 +556,11 @@ gcd(poly([1,1,2]), poly([1,2,3])) # returns (x-1)*(x-2)
543556
```
544557
"""
545558
function gcd{T, S}(a::Poly{T}, b::Poly{S})
546-
if reduce(&, (@compat abs.(b.a)) .<=2*eps(S))
547-
return a
548-
else
549-
s, r = divrem(a, b)
550-
return gcd(b, r)
551-
end
559+
R = typeof(one(T)/one(S))
560+
degree(b) == 0 && b zero(b) && return convert(Poly{R}, a)
561+
562+
s, r = divrem(a, b)
563+
return gcd(b, r)
552564
end
553565

554566

test/runtests.jl

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,5 +289,27 @@ p = Poly([0,one(Float64)])
289289
@test Poly{Complex{Float64}} == typeof(p+1im)
290290
@test Poly{Complex{Float64}} == typeof(1im-p)
291291
@test Poly{Complex{Float64}} == typeof(p*1im)
292+
293+
## comparison between `Number`s and `Poly`s
294+
p1s = Poly([1,2], :s)
295+
p1x = Poly([1,2], :x)
296+
p2s = Poly([1], :s)
297+
298+
@test p1s == p1s
299+
@test p1s p1x
300+
@test p1s p2s
301+
302+
@test_throws ErrorException p1s p1x
303+
@test p1s p2s
304+
@test p1s Poly([1,2.], :s)
305+
306+
@test p2s 1. p2s
307+
@test p2s == 1. == p2s
308+
@test p2s 2. p2s
309+
@test p1s 2. p1s
310+
311+
@test nnz(map(Poly, speye(5,5))) == 5
312+
292313
@test Poly([0.5]) + 2 == Poly([2.5])
293314
@test 2 - Poly([0.5]) == Poly([1.5])
315+

0 commit comments

Comments
 (0)