Skip to content

Commit 83297a5

Browse files
committed
Add division with remainder
1 parent 8dd49dd commit 83297a5

File tree

8 files changed

+137
-44
lines changed

8 files changed

+137
-44
lines changed

src/MultivariatePolynomials.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ include("subs.jl")
3232
include("algebraicset.jl")
3333
include("norm.jl")
3434

35+
include("div.jl")
36+
3537
include("show.jl")
3638

3739
end # module

src/calg.jl

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,7 @@
44
# Any op T # ambig with Polytype op Any
55
# PolyType op T # breaks ambig
66

7-
import Base.(^), Base.dot, Base.(.+), Base.(.-), Base.(.*), Base.(./), Base.(.^)
8-
9-
(.+)(p::PolyType, α) = p+α
10-
(.+)(α, p::PolyType) = α+p
11-
(.+)(p::PolyType, q::PolyType) = p+q
12-
(.-)(p::PolyType, α) = p-α
13-
(.-)(α, p::PolyType) = α-p
14-
(.-)(p::PolyType, q::PolyType) = p-q
15-
(.*)(p::PolyType, α) = p*α
16-
(.*)(α, p::PolyType) = α*p
17-
(.*)(p::PolyType, q::PolyType) = p*q
18-
(./)(p::PolyType, α) = p/α
19-
(./)(α, p::PolyType) = α/p
20-
(./)(p::PolyType, q::PolyType) = p/q
21-
(.^)(p::PolyType, i::Int) = p^i
7+
import Base.(^), Base.dot
228

239
import Base.transpose
2410
Base.transpose(p::PolyType) = p
@@ -55,28 +41,28 @@ function (*)(x::PolyVar{true}, y::MonomialVector{true})
5541
w, updatez = multiplyvar(y.vars, x)
5642
MonomialVector{true}(w, updatez.(y.Z))
5743
end
58-
function multiplymono(v::Vector{PolyVar{true}}, x::Monomial{true})
44+
function multdivmono(v::Vector{PolyVar{true}}, x::Monomial{true}, op)
5945
if v == x.vars
6046
# /!\ no copy done here for efficiency, do not mess up with vars
6147
w = v
62-
updatez = z -> z + x.z
48+
updatez = z -> op(z, x.z)
6349
else
6450
w, maps = myunion([v, x.vars])
6551
updatez = z -> begin
6652
newz = zeros(Int, length(w))
67-
newz[maps[1]] += z
68-
newz[maps[2]] += x.z
53+
newz[maps[1]] = op(newz[maps[1]], z)
54+
newz[maps[2]] = op(newz[maps[2]], x.z)
6955
newz
7056
end
7157
end
7258
w, updatez
7359
end
7460
function (*)(x::Monomial{true}, y::Monomial{true})
75-
w, updatez = multiplymono(y.vars, x)
61+
w, updatez = multdivmono(y.vars, x, +)
7662
Monomial{true}(w, updatez(y.z))
7763
end
7864
function (*)(x::Monomial{true}, y::MonomialVector{true})
79-
w, updatez = multiplymono(y.vars, x)
65+
w, updatez = multdivmono(y.vars, x, +)
8066
MonomialVector{true}(w, updatez.(y.Z))
8167
end
8268
(*)(x::Monomial{true}, y::PolyVar{true}) = y * x

src/div.jl

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# _div(a, b) assumes that b divides a
2+
function _div(m1::Monomial{true}, m2::Monomial{true})
3+
w, updatez = multdivmono(m1.vars, m2, -)
4+
Monomial{true}(w, updatez(m1.z))
5+
end
6+
function _div(t::Term, m::Monomial)
7+
t1.α * _div(monomial(t), m)
8+
end
9+
function _div(t1::Term, t2::Term)
10+
(t1.α / t2.α) * _div(monomial(t1), monomial(t2))
11+
end
12+
13+
proddiff(x, y) = x*y - x*y
14+
15+
function Base.divrem{C, T, S}(f::Polynomial{C, T}, g::Polynomial{C, S})
16+
rf = Polynomial{C, promote_op(proddiff, T, S)}(f)
17+
q = r = zero(f - g / 2)
18+
lt = leadingterm(g)
19+
rg = removeleadingterm(g)
20+
lm = monomial(lt)
21+
while !iszero(rf)
22+
ltf = leadingterm(rf)
23+
if divides(lm, ltf)
24+
qt = _div(ltf, lt)
25+
q += qt
26+
rf = removeleadingterm(rf) - qt * rg
27+
elseif lm > monomial(ltf)
28+
# Since the monomials are sorted in decreasing order,
29+
# lm is larger than all of them hence it cannot divide any of them
30+
r += rf
31+
break
32+
else
33+
r += ltf
34+
rf = removeleadingterm(rf)
35+
end
36+
end
37+
q, r
38+
end
39+
Base.div(f::PolyType, g::PolyType) = divrem(f, g)[1]
40+
Base.rem(f::PolyType, g::PolyType) = divrem(f, g)[2]

src/measure.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ type Measure{C, T}
1010
a::Vector{T}
1111
x::MonomialVector{C}
1212

13-
function Measure(a::Vector{T}, x::MonomialVector{C})
13+
function Measure{C, T}(a::Vector{T}, x::MonomialVector{C}) where {C, T}
1414
if length(a) != length(x)
1515
error("There should be as many coefficient than monomials")
1616
end

src/mono.jl

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ end
3030
immutable PolyVar{C} <: PolyType{C}
3131
id::Int
3232
name::AbstractString
33-
function PolyVar(name::AbstractString)
33+
function PolyVar{C}(name::AbstractString) where {C}
3434
# gensym returns something like Symbol("##42")
3535
# we first remove "##" and then parse it into an Int
3636
id = parse(Int, string(gensym())[3:end])
@@ -84,7 +84,7 @@ type Monomial{C} <: PolyType{C}
8484
vars::Vector{PolyVar{C}}
8585
z::Vector{Int}
8686

87-
function Monomial(vars::Vector{PolyVar{C}}, z::Vector{Int})
87+
function Monomial{C}(vars::Vector{PolyVar{C}}, z::Vector{Int}) where {C}
8888
if length(vars) != length(z)
8989
throw(ArgumentError("There should be as many vars than exponents"))
9090
end
@@ -122,12 +122,35 @@ isconstant(x::Monomial) = deg(x) == 0
122122
zero{C}(::Type{Monomial{C}}) = zero(PolyType{C})
123123
one{C}(::Type{Monomial{C}}) = one(PolyType{C})
124124

125+
monomial(m::Monomial) = m
126+
# Does m1 divides m2 ?
127+
function divides(m1::Monomial, m2::Monomial)
128+
i = j = 1
129+
while i <= length(m1.z) && j <= length(m2.z)
130+
if m1.vars[i] == m2.vars[j]
131+
if m1.z[i] > m2.z[j]
132+
return false
133+
end
134+
i += 1
135+
j += 1
136+
elseif m1.vars[i] > m2.vars[j]
137+
if !iszero(m1.z[i])
138+
return false
139+
end
140+
i += 1
141+
else
142+
j += 1
143+
end
144+
end
145+
i > length(m1.z)
146+
end
147+
125148
# Invariant: Always sorted and no zero vector
126149
type MonomialVector{C} <: PolyType{C}
127150
vars::Vector{PolyVar{C}}
128151
Z::Vector{Vector{Int}}
129152

130-
function MonomialVector(vars::Vector{PolyVar{C}}, Z::Vector{Vector{Int}})
153+
function MonomialVector{C}(vars::Vector{PolyVar{C}}, Z::Vector{Vector{Int}}) where {C}
131154
for z in Z
132155
if length(vars) != length(z)
133156
throw(ArgumentError("There should be as many vars than exponents"))
@@ -166,11 +189,12 @@ function getindex{MV<:MonomialVector}(x::MV, I)
166189
MV(x.vars, x.Z[sort(I)])
167190
end
168191

169-
length(x::MonomialVector) = length(x.Z)
170-
isempty(x::MonomialVector) = length(x) == 0
171-
start(::MonomialVector) = 1
172-
done(x::MonomialVector, state) = length(x) < state
173-
next(x::MonomialVector, state) = (x[state], state+1)
192+
Base.endof(x::MonomialVector) = length(x)
193+
Base.length(x::MonomialVector) = length(x.Z)
194+
Base.isempty(x::MonomialVector) = length(x) == 0
195+
Base.start(::MonomialVector) = 1
196+
Base.done(x::MonomialVector, state) = length(x) < state
197+
Base.next(x::MonomialVector, state) = (x[state], state+1)
174198

175199
extdeg(x::MonomialVector) = extrema(sum.(x.Z))
176200
mindeg(x::MonomialVector) = minimum(sum.(x.Z))
@@ -289,7 +313,7 @@ function sortmonovec{C, T<:Union{PolyType,Int}}(::Type{PolyVar{C}}, X::Vector{T}
289313
σ, MonomialVector{C}(allvars, Z[σ])
290314
end
291315
end
292-
typealias VectorOfPolyType{C} Union{PolyType{C},Int}
316+
VectorOfPolyType{C} = Union{PolyType{C},Int}
293317
sortmonovec{T<:VectorOfPolyType{false}}(x::Vector{T}) = sortmonovec(PolyVar{false}, x)
294318
sortmonovec{T<:VectorOfPolyType{true}}(x::Vector{T}) = sortmonovec(PolyVar{true}, x)
295319
function (::Type{MonomialVector{C}}){C}(X::Vector)

src/poly.jl

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
export Term, Polynomial, MatPolynomial, SOSDecomposition, TermType, getmat, monomials, removemonomials
1+
export Term, Polynomial, MatPolynomial, SOSDecomposition, TermType
2+
export monomial, monomials, removeleadingterm, removemonomials
3+
export leadingcoef, leadingmonomial, leadingterm
4+
export getmat, divides
25

36
@compat abstract type TermType{C, T} <: PolyType{C} end
47
eltype{C, T}(::Type{TermType{C, T}}) = T
@@ -65,6 +68,9 @@ done(::Term, state) = state
6568
next(t::Term, state) = (t, true)
6669
getindex(t::Term, I::Int) = t
6770

71+
monomial(t::Term) = t.x
72+
divides(t1::Union{Term, Monomial}, t2::Union{Term, Monomial}) = divides(monomial(t1), monomial(t2))
73+
6874
# Invariant:
6975
# a and x might be empty: meaning it is the zero polynomial
7076
# a does not contain any zeros
@@ -73,9 +79,8 @@ type Polynomial{C, T} <: TermContainer{C, T}
7379
a::Vector{T}
7480
x::MonomialVector{C}
7581

76-
function Polynomial(a::Vector{T}, x::MonomialVector{C})
77-
if length(a) != length(x)
78-
throw(ArgumentError("There should be as many coefficient than monomials"))
82+
function Polynomial{C, T}(a::Vector{T}, x::MonomialVector{C}) where {C, T}
83+
if length(a) != length(x) throw(ArgumentError("There should be as many coefficient than monomials"))
7984
end
8085
zeroidx = Int[]
8186
for (i,α) in enumerate(a)
@@ -90,7 +95,7 @@ type Polynomial{C, T} <: TermContainer{C, T}
9095
a = a[nzidx]
9196
x = x[nzidx]
9297
end
93-
new(a, x)
98+
new{C, T}(a, x)
9499
end
95100
end
96101
iscomm{C, T}(::Type{Polynomial{C, T}}) = C
@@ -172,18 +177,26 @@ end
172177
vars(p::Polynomial) = vars(p.x)
173178
nvars(p::Polynomial) = nvars(p.x)
174179

175-
length(p::Polynomial) = length(p.a)
176-
isempty(p::Polynomial) = isempty(p.a)
177-
start(::Polynomial) = 1
178-
done(p::Polynomial, state) = length(p) < state
179-
next(p::Polynomial, state) = (p[state], state+1)
180+
Base.endof(p::Polynomial) = length(p)
181+
Base.length(p::Polynomial) = length(p.a)
182+
Base.isempty(p::Polynomial) = isempty(p.a)
183+
Base.start(::Polynomial) = 1
184+
Base.done(p::Polynomial, state) = length(p) < state
185+
Base.next(p::Polynomial, state) = (p[state], state+1)
180186

181187
extdeg(p::Polynomial) = extdeg(p.x)
182188
mindeg(p::Polynomial) = mindeg(p.x)
183189
maxdeg(p::Polynomial) = maxdeg(p.x)
184190

191+
leadingcoef(p::Polynomial) = first(p.a)
192+
leadingmonomial(p::Polynomial) = first(p.x)
193+
leadingterm(p::Polynomial) = first(p)
194+
185195
monomials(p::Polynomial) = p.x
186196

197+
function removeleadingterm(p::Polynomial)
198+
Polynomial(p.a[2:end], p.x[2:end])
199+
end
187200
function removemonomials(p::Polynomial, x::MonomialVector)
188201
# use the fact that monomials are sorted to do this O(n) instead of O(n^2)
189202
j = 1
@@ -347,14 +360,14 @@ TermContainer(p::MatPolynomial) = Polynomial(p)
347360

348361
type SOSDecomposition{C, T} <: TermType{C, T}
349362
ps::Vector{Polynomial{C, T}}
350-
function SOSDecomposition(ps::Vector{Polynomial{C, T}})
363+
function SOSDecomposition{C, T}(ps::Vector{Polynomial{C, T}}) where {C, T}
351364
new(ps)
352365
end
353366
end
354-
function (::Type{SOSDecomposition{C, T}}){C, T}(ps::Vector)
367+
function SOSDecomposition{C, T}(ps::Vector) where {C, T}
355368
SOSDecomposition(Vector{Polynomial{C, T}}(ps))
356369
end
357-
function (::Type{SOSDecomposition{C}}){C}(ps::Vector)
370+
function SOSDecomposition{C}(ps::Vector) where {C}
358371
T = reduce(promote_type, Int, map(eltype, ps))
359372
SOSDecomposition{C, T}(ps)
360373
end

test/div.jl

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
@testset "Division" begin
2+
# Taken from
3+
# Ideals, Varieties, and Algorithms
4+
# Cox, Little and O'Shea, Fourth edition
5+
# They have been adapted to the grlex ordering
6+
@testset "Divides" begin
7+
@polyvar x y z
8+
@test divides(x*y, x^2*y)
9+
@test divides(x*y, x*y^2)
10+
@test divides(y*z, x*y*z)
11+
@test !divides(y*z, x*z)
12+
@test !divides(x^2*y, x*y^2)
13+
end
14+
@testset "Leading function" begin
15+
@polyvar x y z
16+
# See page 60
17+
p = 4x*y^2*z + 4z^2 + 7x^2*z^2 - 5x^3
18+
@test leadingcoef(p) == 7
19+
@test leadingmonomial(p) == x^2*z^2
20+
@test leadingterm(p) == 7x^2*z^2
21+
end
22+
@testset "Division examples" begin
23+
@polyvar x y
24+
@test div(x*y^2 + 1, x*y + 1) == y
25+
@test rem(x*y^2 + 1, x*y + 1) == -y + 1
26+
end
27+
end

test/runtests.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ include("subs.jl")
1717
include("algebraicset.jl")
1818
include("norm.jl")
1919
include("hash.jl")
20+
include("div.jl")
2021

2122
include("show.jl")
2223

0 commit comments

Comments
 (0)