Skip to content

Commit 0203f83

Browse files
authored
Merge pull request #92 from JuliaAlgebra/pretty-printing
Add unicode output for polynomials
2 parents 80eb4b3 + 8f81e53 commit 0203f83

File tree

4 files changed

+119
-36
lines changed

4 files changed

+119
-36
lines changed

src/show.jl

+92-23
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,82 @@
1-
function Base.show(io::IO, v::AbstractVariable)
2-
print(io, name(v))
1+
Base.print(io::IO, v::AbstractPolynomialLike) = show(io, MIME"text/print"(), v)
2+
3+
# VARIABLES
4+
Base.show(io::IO, v::AbstractVariable) = print_var(io, MIME"text/plain"(), v)
5+
Base.show(io::IO, mime::MIME"text/plain", v::AbstractVariable) = print_var(io, mime, v)
6+
Base.show(io::IO, mime::MIME"text/latex", v::AbstractVariable) = print_var(io, mime, v)
7+
Base.show(io::IO, mime::MIME"text/print", v::AbstractVariable) = print_var(io, mime, v)
8+
9+
function print_var(io::IO, mime::MIME, var::AbstractVariable)
10+
base, indices = name_base_indices(var)
11+
if isempty(indices)
12+
print(io, base)
13+
else
14+
print(io, base)
15+
print_subscript(io, mime, indices)
16+
end
317
end
18+
print_var(io::IO, mime::MIME"text/print", var::AbstractVariable) = print(io, name(var))
419

5-
if VERSION < v"0.7.0-DEV.1144" # Define `isone` for base types JuliaLang/julia#22846
6-
isone(x::T) where T = x == one(T)
20+
function print_subscript(io::IO, ::MIME"text/latex", index)
21+
print(io, "_{", join(index, ","), "}")
722
end
23+
function print_subscript(io::IO, mime, indices)
24+
if length(indices) == 1
25+
print(io, unicode_subscript(indices[1]))
26+
else
27+
print(io, join(unicode_subscript.(indices), "\u208B"))
28+
end
29+
end
30+
31+
const unicode_subscripts = ("","","","","","","","","","")
32+
unicode_subscript(i) = unicode_subscripts[i+1]
833

9-
function Base.show(io::IO, m::AbstractMonomial)
34+
# MONOMIALS
35+
36+
Base.show(io::IO, mime::MIME"text/latex", m::AbstractMonomial) = print_monomial(io, mime, m)
37+
Base.show(io::IO, mime::MIME"text/plain", m::AbstractMonomial) = print_monomial(io, mime, m)
38+
Base.show(io::IO, mime::MIME"text/print", m::AbstractMonomial) = print_monomial(io, mime, m)
39+
Base.show(io::IO, m::AbstractMonomial) = print_monomial(io, MIME"text/plain"(), m)
40+
41+
function print_monomial(io::IO, mime, m::AbstractMonomial)
1042
if isconstant(m)
1143
print(io, '1')
1244
else
13-
for (var, exp) in zip(variables(m), exponents(m))
45+
printed_var = false
46+
vars = variables(m)
47+
n = length(vars)
48+
for (i, var, exp) in zip(1:n, vars, exponents(m))
1449
if !iszero(exp)
15-
print(io, var)
50+
if mime isa MIME"text/print" && printed_var && i > 0 &&
51+
print(io,"*")
52+
end
53+
print_var(io, mime, var)
54+
printed_var = true
1655
if !isone(exp)
17-
print(io, '^', exp)
56+
print_exponent(io, mime, exp)
1857
end
1958
end
2059
end
2160
end
2261
end
62+
#
63+
print_exponent(io::IO, ::MIME"text/latex", exp) = print(io, "^{", exp, "}")
64+
print_exponent(io::IO, ::MIME"text/print", exp) = print(io, "^", exp)
65+
function print_exponent(io::IO, mime, exp)
66+
print(io, join(unicode_superscript.(reverse(digits(exp)))))
67+
end
2368

24-
should_print_coefficient(x) = true # By default, just print all coefficients
25-
should_print_coefficient(x::Number) = !isone(x) # For numbers, we omit any "one" coefficients
26-
print_coefficient(io::IO, coeff::Real) = print(io, coeff)
27-
print_coefficient(io::IO, coeff) = print(io, "(", coeff, ")")
69+
const unicode_superscripts = ("","¹","²","³","","","","","","")
70+
unicode_superscript(i) = unicode_superscripts[i+1]
2871

29-
function Base.show(io::IO, t::AbstractTerm)
72+
# TERM
73+
74+
Base.show(io::IO, t::AbstractTerm) = print_term(io, MIME"text/plain"(), t)
75+
Base.show(io::IO, mime::MIME"text/latex", t::AbstractTerm) = print_term(io, mime, t)
76+
Base.show(io::IO, mime::MIME"text/plain", t::AbstractTerm) = print_term(io, mime, t)
77+
Base.show(io::IO, mime::MIME"text/print", t::AbstractTerm) = print_term(io, mime, t)
78+
79+
function print_term(io::IO, mime, t::AbstractTerm)
3080
if isconstant(t)
3181
print_coefficient(io, coefficient(t))
3282
else
@@ -38,36 +88,55 @@ function Base.show(io::IO, t::AbstractTerm)
3888
end
3989
end
4090
if !iszero(t)
41-
print(io, monomial(t))
91+
show(io, mime, monomial(t))
4292
end
4393
end
4494
end
4595

46-
isnegative(x::Real) = x < 0
47-
isnegative(x) = false
96+
should_print_coefficient(x) = true # By default, just print all coefficients
97+
should_print_coefficient(x::Number) = !isone(x) # For numbers, we omit any "one" coefficients
98+
print_coefficient(io::IO, coeff::Real) = print(io, coeff)
99+
print_coefficient(io::IO, coeff) = print(io, "(", coeff, ")")
48100

49-
function Base.show(io::IO, p::AbstractPolynomial{T}) where T
101+
# POLYNOMIAL
102+
103+
Base.show(io::IO, t::AbstractPolynomial) = print_poly(io, MIME"text/plain"(), t)
104+
Base.show(io::IO, mime::MIME"text/plain", t::AbstractPolynomial) = print_poly(io, mime, t)
105+
Base.show(io::IO, mime::MIME"text/latex", t::AbstractPolynomial) = print_poly(io, mime, t)
106+
Base.show(io::IO, mime::MIME"text/print", t::AbstractPolynomial) = print_poly(io, mime, t)
107+
108+
function print_poly(io::IO, mime, p::AbstractPolynomial{T}) where T
50109
ts = terms(p)
51110
if isempty(ts)
52111
print(io, zero(T))
53112
else
54-
print(io, first(ts))
113+
print_term(io, mime, first(ts))
55114
for t in Iterators.drop(ts, 1)
56115
if isnegative(coefficient(t))
57116
print(io, " - ")
58-
print(io, abs(coefficient(t)) * monomial(t))
117+
show(io, mime, abs(coefficient(t)) * monomial(t))
59118
else
60119
print(io, " + ")
61-
print(io, t)
120+
show(io, mime, t)
62121
end
63122
end
64123
end
65124
end
66125

67-
function Base.show(io::IO, p::RationalPoly)
126+
isnegative(x::Real) = x < 0
127+
isnegative(x) = false
128+
129+
# RATIONAL POLY
130+
131+
Base.show(io::IO, t::RationalPoly) = print_ratpoly(io, MIME"text/plain"(), t)
132+
Base.show(io::IO, mime::MIME"text/plain", t::RationalPoly) = print_ratpoly(io, mime, t)
133+
Base.show(io::IO, mime::MIME"text/latex", t::RationalPoly) = print_ratpoly(io, mime, t)
134+
Base.show(io::IO, mime::MIME"text/print", t::RationalPoly) = print_ratpoly(io, mime, t)
135+
136+
function print_ratpoly(io::IO, mime, p::RationalPoly)
68137
print(io, "(")
69-
print(io, p.num)
138+
show(io, mime, p.num)
70139
print(io, ") / (")
71-
print(io, p.den)
140+
show(io, mime, p.den)
72141
print(io, ")")
73142
end

test/REQUIRE

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
BenchmarkTools
22
DynamicPolynomials 0.1
3+
TypedPolynomials 0.2

test/runtests.jl

+2
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@ function try_import(name::Symbol)
1616
end
1717
end
1818

19+
global implementation = :DynamicPolynomials
1920
if try_import(:DynamicPolynomials)
2021
Mod = DynamicPolynomials
2122
include("commutativetests.jl")
2223
include("noncommutativetests.jl")
2324
end
2425

26+
global implementation = :TypedPolynomials
2527
if try_import(:TypedPolynomials)
2628
Mod = TypedPolynomials
2729
include("commutativetests.jl")

test/show.jl

+24-13
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,39 @@
11
@testset "Show" begin
22
Mod.@polyvar x y z
3-
@test sprint(show, (x*y^2 + x + 1 + y)) == "xy^2 + x + y + 1"
4-
@test sprint(show, (x + 1 + y) / x^2) == "(x + y + 1) / (x^2)"
5-
@test sprint(show, (x - y - x + y) / (x^2 - x)) == "(0) / (x^2 - x)"
3+
@test sprint(show, (x*y^2 + x + 1 + y)) == "xy² + x + y + 1"
4+
@test sprint(show, (x + 1 + y) / x^2) == "(x + y + 1) / (x²)"
5+
@test sprint(show, (x - y - x + y) / (x^2 - x)) == "(0) / (x² - x)"
66
# Test taken from TypedPolynomials
77
@test sprint(show, x) == "x"
88
@test sprint(show, x^0) == "1"
99
@test sprint(show, x^1) == "x"
10-
@test sprint(show, x^2) == "x^2"
11-
@test sprint(show, 1x^2) == "x^2"
10+
@test sprint(show, x^2) == "x²"
11+
@test sprint(show, 1x^2) == "x²"
1212
@test sprint(show, 5x) == "5x"
1313
@test sprint(show, x * y) == "xy"
1414
@test sprint(show, y * x) == "xy"
1515
@test sprint(show, 5 + y + x) == "x + y + 5"
1616
@test sprint(show, y + 5 + x) == "x + y + 5"
17-
@test sprint(show, x + x^2) == "x^2 + x"
18-
@test sprint(show, x^2 + x) == "x^2 + x"
19-
@test sprint(show, x^2 - 3.0x) == "x^2 - 3.0x"
20-
@test sprint(show, -x^3) == "-x^3"
21-
@test sprint(show, -x^3 + y^2 - x) == "-x^3 + y^2 - x"
22-
@test sprint(show, -2.0x^2) == "-2.0x^2"
17+
@test sprint(show, x + x^2) == "x² + x"
18+
@test sprint(show, x^2 + x) == "x² + x"
19+
@test sprint(show, x^2 - 3.0x) == "x² - 3.0x"
20+
@test sprint(show, -x^3) == "-x³"
21+
@test sprint(show, -x^3 + y^2 - x) == "-x³ + y² - x"
22+
@test sprint(show, -2.0x^2) == "-2.0x²"
2323
@test sprint(show, (1.0 + 3.1im) * x*z) == "(1.0 + 3.1im)xz"
2424
@test sprint(show, -(1.0 + 3.1im) * z*x) == "(-1.0 - 3.1im)xz"
25-
@test sprint(show, x^2 + (1.0 + 3.1im) * x) == "x^2 + (1.0 + 3.1im)x"
26-
@test sprint(show, x^2 - (1.0 + 3.1im) * x) == "x^2 + (-1.0 - 3.1im)x"
25+
@test sprint(show, x^2 + (1.0 + 3.1im) * x) == "x² + (1.0 + 3.1im)x"
26+
@test sprint(show, x^2 - (1.0 + 3.1im) * x) == "x² + (-1.0 - 3.1im)x"
2727
@test sprint(show, [1.0, 2.0] * x) == "([1.0, 2.0])x"
28+
29+
Mod.@polyvar x[0:9]
30+
@test sprint(show, sum(i*x[i]^i for i=1:10)) == "10x₉¹⁰ + 9x₈⁹ + 8x₇⁸ + 7x₆⁷ + 6x₅⁶ + 5x₄⁵ + 4x₃⁴ + 3x₂³ + 2x₁² + x₀"
31+
@test sprint(show, "text/latex", sum(i*x[i]^i for i=1:10)) == "10x_{9}^{10} + 9x_{8}^{9} + 8x_{7}^{8} + 7x_{6}^{7} + 6x_{5}^{6} + 5x_{4}^{5} + 4x_{3}^{4} + 3x_{2}^{3} + 2x_{1}^{2} + x_{0}"
32+
33+
#This is only supported by DynamicPolynomials so far
34+
@static if implementation == :DynamicPolynomials
35+
Mod.@polyvar A[1:2, 1:2]
36+
@test sprint(show, sum(A)) == "A₁₋₁ + A₂₋₁ + A₁₋₂ + A₂₋₂"
37+
@test sprint(show, "text/latex", sum(A)) == "A_{1,1} + A_{2,1} + A_{1,2} + A_{2,2}"
38+
end
2839
end

0 commit comments

Comments
 (0)