Skip to content

Commit 9ce8287

Browse files
authored
Merge pull request #73 from Keno/showfix
Change show.jl to be more modular
2 parents 2557660 + 7f593c1 commit 9ce8287

File tree

3 files changed

+242
-67
lines changed

3 files changed

+242
-67
lines changed

src/Polynomials.jl

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ immutable Poly{T<:Number}
6969
return new(zeros(T,1), @compat Symbol(var))
7070
else
7171
# determine the last nonzero element and truncate a accordingly
72-
a_last = max(1,findlast(a))
72+
a_last = max(1,findlast(x->x!=zero(T), a))
7373
new(a[1:a_last], @compat Symbol(var))
7474
end
7575
end
@@ -93,7 +93,7 @@ poly([1,2,3]) # Poly(-6 + 11x - 6x^2 + x^3)
9393
function poly{T}(r::AbstractVector{T}, var=:x)
9494
n = length(r)
9595
c = zeros(T, n+1)
96-
c[1] = 1
96+
c[1] = one(T)
9797
for j = 1:n
9898
for i = j:-1:1
9999
c[i+1] = c[i+1]-r[j]*c[i]
@@ -148,15 +148,25 @@ Return the indeterminate of a polynomial, `x`.
148148
* `variable([var::Symbol])`: return polynomial 1x over `Float64`.
149149
150150
"""
151-
variable{T}(p::Poly{T}) = poly(zeros(T,1), p.var)
152-
variable{T<:Number}(::Type{T}, var=:x) = poly(zeros(T,1), var)
153-
variable(var::Symbol=:x) = poly([0.0], var)
151+
variable{T<:Number}(::Type{T}, var=:x) = Poly([zero(T), one(T)], var)
152+
variable{T}(p::Poly{T}) = variable(T, p.var)
153+
variable(var::Symbol=:x) = variable(Float64, var)
154154

155155
"""
156156
157157
`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
158158
159159
"""
160+
function truncate{T}(p::Poly{Complex{T}}; reltol = eps(T), abstol = eps(T))
161+
a = coeffs(p)
162+
amax = maxabs(a)
163+
thresh = amax * reltol + abstol
164+
anew = map(ai -> complex(abs(real(ai)) <= thresh ? zero(T) : real(ai),
165+
abs(imag(ai)) <= thresh ? zero(T) : imag(ai)),
166+
a)
167+
return Poly(anew, p.var)
168+
end
169+
160170
function truncate{T}(p::Poly{T}; reltol = eps(T), abstol = eps(T))
161171
a = coeffs(p)
162172
amax = maxabs(a)
@@ -218,7 +228,9 @@ function setindex!(p::Poly, vs, idx::AbstractArray)
218228
[setindex!(p, v, i) for (i,v) in zip(idx, vs)]
219229
p
220230
end
231+
Base.eachindex{T}(p::Poly{T}) = 0:(length(p)-1)
221232

233+
222234
copy(p::Poly) = Poly(copy(p.a), p.var)
223235

224236
zero{T}(p::Poly{T}) = Poly([zero(T)], p.var)
@@ -229,6 +241,8 @@ one{T}(::Type{Poly{T}}) = Poly([one(T)])
229241
## Overload arithmetic operators for polynomial operations between polynomials and scalars
230242
*{T<:Number,S}(c::T, p::Poly{S}) = Poly(c * p.a, p.var)
231243
*{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
232246
.*{T<:Number,S}(c::T, p::Poly{S}) = Poly(c * p.a, p.var)
233247
.*{T<:Number,S}(p::Poly{S}, c::T) = Poly(p.a * c, p.var)
234248
/(p::Poly, c::Number) = Poly(p.a / c, p.var)
@@ -356,7 +370,7 @@ function polyval{T,S}(p::Poly{T}, x::S)
356370
if lenp == 0
357371
return zero(R) * x
358372
else
359-
y = convert(R, p[end]) + 0*x
373+
y = convert(R, p[end]) + zero(T)*x
360374
for i = (endof(p)-1):-1:0
361375
y = p[i] + x*y
362376
end

src/show.jl

Lines changed: 177 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,199 @@
1+
## Poly{T} is basically T[x], with T a Ring.
2+
## T[x] may not have an order so abs, comparing to 0 may not be defined.
13

2-
function show(io::IO, p::Poly)
3-
print(io,"Poly(")
4-
print(io,p)
5-
print(io,")")
4+
## to handle this case we create some functions
5+
## which can be modified by users for other Ts
6+
7+
"`hasneg(::T)` attribute is true if: `pj < zero(T)` is defined."
8+
hasneg{T}(::Type{T}) = false
9+
10+
"Could value possibly be negative and if so, is it?"
11+
isneg{T}(pj::T) = hasneg(T) && pj < zero(T)
12+
13+
"Make `pj` positive if it is negative. (Don't call `abs` as that may not be defined, or appropriate.)"
14+
aspos{T}(pj::T) = (hasneg(T) && isneg(pj)) ? -pj : pj
15+
16+
"Should a value of `one(T)` be shown as a coefficient of monomial `x^i`, `i >= 1`? (`1.0x^2` is shown, `1 x^2` is not)"
17+
showone{T}(::Type{T}) = true
18+
19+
20+
#####
21+
22+
## Numbers
23+
hasneg{T<:Real}(::Type{T}) = true
24+
25+
### Integer
26+
showone{T<:Integer}(::Type{T}) = false
27+
showone{T}(::Type{Rational{T}}) = false
28+
29+
30+
31+
32+
### Complex coefficients
33+
hasneg{T}(::Type{Complex{T}}) = true ## we say neg if real(z) < 0 || real(z) == 0 and imag(g) < 0
34+
35+
function isneg{T}(pj::Complex{T})
36+
real(pj) < 0 && return true
37+
(real(pj) == 0 && imag(pj) < 0) && return(true)
38+
return false
639
end
740

8-
function printexponent(io,var,i)
9-
if i == 0
10-
elseif i == 1
11-
print(io,var)
12-
else
13-
print(io,var,"^",i)
41+
showone{T}(pj::Type{Complex{T}}) = showone(T)
42+
43+
44+
### Polynomials as coefficients
45+
hasneg{S}(::Type{Poly{S}}) = false
46+
showone{S}(::Type{Poly{S}}) = false
47+
48+
49+
#####
50+
51+
"Show different operations depending on mimetype. `l-` is leading minus sign."
52+
function showop(::MIME"text/plain", op)
53+
d = Dict("*" => "", "+" => " + ", "-" => " - ", "l-" => "-")
54+
d[op]
55+
end
56+
57+
function showop(::MIME"text/latex", op)
58+
d = Dict("*" => "\\cdot ", "+" => " + ", "-" => " - ", "l-" => "-")
59+
d[op]
60+
end
61+
62+
function showop(::MIME"text/html", op)
63+
d = Dict("*" => "&#8729;", "+" => " &#43; ", "-" => " &#45; ", "l-" => "&#45;")
64+
d[op]
65+
end
66+
67+
68+
69+
###
70+
71+
function printpoly{T}(io::IO, p::Poly{T}, mimetype)
72+
first = true
73+
printed_anything = false
74+
for i in eachindex(p)
75+
printed = showterm(io,p,i,first, mimetype)
76+
first &= !printed
77+
printed_anything |= printed
1478
end
79+
printed_anything || print(io, zero(T))
1580
end
1681

17-
function printterm{T}(io::IO,p::Poly{T},j,first)
82+
function showterm{T}(io::IO,p::Poly{T},j,first, mimetype)
1883
pj = p[j]
19-
if pj == zero(T)
20-
return false
21-
end
22-
neg = pj < zero(T)
23-
if first
24-
neg && print(io, "-") #Prepend - if first and negative
25-
else
26-
neg ? print(io, " - ") : print(io," + ")
27-
end
28-
pj = abs(pj)
29-
if pj != one(T) || j == 0
30-
show(io,pj)
31-
end
32-
printexponent(io,p.var,j)
84+
85+
pj == zero(T) && return false
86+
87+
pj = printsign(io, pj, j, first, mimetype)
88+
printcoefficient(io, pj, j, mimetype)
89+
printproductsign(io, pj, j, mimetype)
90+
printexponent(io,p.var,j, mimetype)
3391
true
3492
end
3593

36-
function printterm{T<:Complex}(io::IO,p::Poly{T},j,first)
37-
pj = p[j]
38-
abs_repj = abs(real(pj))
39-
abs_impj = abs(imag(pj))
40-
if abs_repj < 2*eps(T) && abs_impj < 2*eps(T)
41-
return false
42-
end
4394

44-
# We show a negative sign either for any complex number with negative
45-
# real part (and then negate the immaginary part) of for complex
46-
# numbers that are pure imaginary with negative imaginary part
47-
48-
neg = ((abs_repj > 2*eps(T)) && real(pj) < 0) ||
49-
((abs_impj > 2*eps(T)) && imag(pj) < 0)
5095

96+
## print the sign
97+
## returns aspos(pj)
98+
function printsign{T}(io::IO, pj::T, j, first, mimetype)
99+
neg = isneg(pj)
51100
if first
52-
neg && print(io, "-") #Prepend - if first and negative
101+
neg && print(io, showop(mimetype, "l-")) #Prepend - if first and negative
53102
else
54-
neg ? print(io," - ") : print(io," + ")
103+
neg ? print(io, showop(mimetype, "-")) : print(io,showop(mimetype, "+"))
55104
end
105+
106+
aspos(pj)
107+
end
108+
109+
## print * or cdot, ...
110+
function printproductsign{T}(io::IO, pj::T, j, mimetype)
111+
j == 0 && return
112+
(showone(T) || pj != one(T)) && print(io, showop(mimetype, "*"))
113+
end
114+
115+
function printcoefficient{T}(io::IO, pj::Complex{T}, j, mimetype)
116+
117+
hasreal = abs(real(pj)) > 0
118+
hasimag = abs(imag(pj)) > 0
56119

57-
if abs_repj > 2*eps(T) #Real part is not 0
58-
if abs_impj > 2*eps(T) #Imag part is not 0
59-
print(io,'(',neg ? -pj : pj,')')
60-
else
61-
print(io, neg ? -real(pj) : real(pj))
62-
end
120+
if hasreal & hasimag
121+
print(io, '(')
122+
show(io, mimetype, pj)
123+
print(io, ')')
124+
elseif hasreal
125+
a = real(pj)
126+
(showone(T) || a != one(T)) && show(io, mimetype, a)
127+
elseif hasimag
128+
b = imag(pj)
129+
(showone(T) || b != one(T)) && show(io, mimetype, b)
130+
show(io, mimetype, im)
63131
else
64-
if abs_impj > 2*eps(T)
65-
print(io,'(', abs(imag(pj)),"im)")
66-
end
132+
return
67133
end
68-
printexponent(io,p.var,j)
69-
true
70134
end
71135

72-
function print{T}(io::IO, p::Poly{T})
73-
first = true
74-
printed_anything = false
75-
n = length(p)-1
76-
for i = 0:n
77-
printed = printterm(io,p,i,first)
78-
first &= !printed
79-
printed_anything |= printed
136+
137+
## show a single term
138+
function printcoefficient{T}(io::IO, pj::T, j, mimetype)
139+
pj == one(T) && !(showone(T) || j == 0) && return
140+
show(io, mimetype, pj)
141+
end
142+
143+
## show exponent
144+
function printexponent(io,var,i, mimetype::MIME"text/latex")
145+
if i == 0
146+
return
147+
elseif i == 1
148+
print(io,var)
149+
else
150+
print(io,var,"^{$i}")
151+
end
152+
end
153+
154+
function printexponent(io,var,i, mimetype)
155+
if i == 0
156+
return
157+
elseif i == 1
158+
print(io,var)
159+
else
160+
print(io,var,"^",i)
80161
end
81-
printed_anything || print(io,zero(T))
162+
end
163+
164+
165+
####
166+
167+
## text/plain
168+
@compat Base.show{T}(io::IO, p::Poly{T}) = show(io, MIME("text/plain"), p)
169+
@compat function Base.show{T}(io::IO, mimetype::MIME"text/plain", p::Poly{T})
170+
print(io,"Poly(")
171+
printpoly(io, p, mimetype)
172+
print(io,")")
173+
174+
end
175+
176+
## text/latex
177+
@compat function Base.show{T}(io::IO, mimetype::MIME"text/latex", p::Poly{T})
178+
print(io, "\$")
179+
printpoly(io, p, mimetype)
180+
print(io, "\$")
181+
end
182+
183+
@compat function Base.show{T}(io::IO, mimetype::MIME"text/latex", a::Rational{T})
184+
abs(a.den) == one(T) ? print(io, a.num) : print(io, "\\frac{$(a.num)}{$(a.den)}")
185+
end
186+
187+
@compat function Base.show{T<:Number}(io::IO, mimetype::MIME"text/latex", a::T)
188+
print(io, a)
189+
end
190+
191+
192+
## text/html
193+
@compat function Base.show{T}(io::IO, mimetype::MIME"text/html", p::Poly{T})
194+
printpoly(io, p, mimetype)
195+
end
196+
197+
@compat function Base.show{T<:Number}(io::IO, mimetype::MIME"text/html", a::T)
198+
print(io, a)
82199
end

test/runtests.jl

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,6 @@ p1 = Poly([4,5,6])
184184
p1[0:1] = [7,8]
185185
@test all(p1[0:end] .== [7,8,6])
186186

187-
188187
## conjugate of poly (issue #59)
189188
as = [im, 1, 2]
190189
bs = [1, 1, 2]
@@ -197,8 +196,53 @@ p2 = convert(Poly{Int64}, p1)
197196
p2[3] = 3
198197
@test p1[3] == 3
199198

199+
200200
## eltype of a Poly type
201201
types = [Int, UInt8, Float64]
202202
for t in types
203203
@test t == eltype(Poly{t})
204204
end
205+
206+
## Polynomials with non-Real type
207+
import Base: +, *, -
208+
immutable Mod2 <: Number
209+
v::Bool
210+
end
211+
+(x::Mod2,y::Mod2) = Mod2(x.v$y.v)
212+
*(x::Mod2,y::Mod2) = Mod2(x.v&y.v)
213+
-(x::Mod2,y::Mod2) = x+y
214+
-(x::Mod2) = x
215+
Base.one(::Type{Mod2}) = Mod2(true)
216+
Base.zero(::Type{Mod2}) = Mod2(false)
217+
Base.convert(::Type{Mod2},x::Integer) = Mod2(convert(Bool,x))
218+
Base.convert(::Type{Bool},x::Mod2) = x.v
219+
220+
# Test that none of this throws
221+
p = Poly([Mod2(true),Mod2(false), Mod2(true)])
222+
repr(p)
223+
@test p(Mod2(false)) == p[0]
224+
225+
226+
## changes to show
227+
p = Poly([1,2,3,1]) # leading coefficient of 1
228+
@test repr(p) == "Poly(1 + 2⋅x + 3⋅x^2 + x^3)"
229+
p = Poly([1.0, 2.0, 3.0, 1.0])
230+
@test repr(p) == "Poly(1.0 + 2.0⋅x + 3.0⋅x^2 + 1.0⋅x^3)"
231+
p = Poly([1+im, 1-im, -1+im, -1 - im])# minus signs
232+
@test repr(p) == "Poly((1 + 1im) + (1 - 1im)⋅x - (1 - 1im)⋅x^2 - (1 + 1im)⋅x^3)"
233+
234+
p = Poly([1,2,3])
235+
@test reprmime("text/latex", p) == "\$1 + 2\\cdot x + 3\\cdot x^{2}\$"
236+
p = Poly([1//2, 2//3, 1])
237+
@test reprmime("text/latex", p) == "\$\\frac{1}{2} + \\frac{2}{3}\\cdot x + x^{2}\$"
238+
239+
240+
## want to be able to copy and paste
241+
string_eval_poly(p,x) = eval(Expr(:function, Expr(:call, :f, :x), parse(string(p)[6:end-1])))(x)
242+
p = Poly([1,2,3]) # copy and paste
243+
q = Poly([1//1, 2//1, 3//1])
244+
r = Poly([1.0, 2, 3])
245+
@test string_eval_poly(p, 5) == p(5)
246+
@test string_eval_poly(q, 5) == q(5)
247+
@test string_eval_poly(r, 5) == r(5)
248+

0 commit comments

Comments
 (0)